Can I set the state of an object to the object itself in a constructor call?

I use a pre-built third-party class library (I think the .NET Framework ...), which contains a Foo class with the following members:

 public sealed class Foo { private object _state; public Foo(object state) { _state = state; } } 

Foo does not contain a public _state installer.

In a specific scenario, I want to set the state of the Foo object to the object itself.

This will NOT compile:

 Foo f; f = new Foo(f); // Compilation error: Use of unassigned local variable 'f' 

Given the above assumptions, is there a way I can set the state of the Foo object for the object itself?


Rationale The Timer class in Windows 8.1 does not contain a Timer(AsyncCallback) constructor Timer(AsyncCallback) that is assigned the state of a Timer object with the object itself. I am trying to pass the .NET code containing this Timer constructor to the Windows 8.1 class library, so my problem is how to pass the object itself to its state member? This question is further described here , but I thought it would be more efficient to raise the main question above as well.

+6
source share
4 answers

Just having Foo is not possible. You can enter the facade / proxy object and pass it to the constructor code, so you can connect everything:

 public class FooFacade { private Foo foo; public void SetFoo(Foo f) { foo = f; } // for each property: public XY { get { return foo.Y; } } } 

Then you can use this facade:

 FooFacade ff = new FooFacade(); Foo f = new Foo(ff); ff.SetFoo(f); 

Of course, this is not what you wanted in the first place. The disadvantage of this attempt is that the state of the object is limited to public representation.

With reflection, just for completeness:

 // create an uninitialized object of type Foo, does not call constructor: var f = (Foo)FormatterServices.GetUninitializedObject(typeof(Foo)); // get field: var stateField = typeof(Foo).GetField("_state", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance); // set value to instance itself, invoke on f: stateField.SetValue(f, f); 
+1
source

Workaround:

 var foo = new Foo(/*params*/); var fieldInfo = foo.GetType().GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance); fieldInfo.SetValue(foo , foo); 
+2
source

If I understand your intent correctly, you need a timer whose callback refers to the timer itself.

 Timer timer = null; timer = new Timer(() => { timer.Stop(); //sample }); 

An object is created using the newobj instruction, which atomically selects and calls the constructor. Without cooperation with ctor, you cannot get a link to an unconstructed object. Therefore, there is no other way to either this approach or reflection.

You can extract the above code into a helper method, make timer local variable, and then each timer callback closes with its private and immutable variable.

 Timer CreateTimer(Action<Timer> callback) { Timer timer = null; timer = new Timer(() => { callback(timer); }); } 
+2
source

So, as I understand it, you do not want to pass the object itself as a constructor parameter; you want to pass a lambda that depends on the object. Is it correct?

In this case, a short but feasible workaround would be to add an indirect layer:

 Action callback = null; var timer = new Timer(() => { if (callback != null) callback(); }); callback = () => { // do something // do something with timer } 

So you have to pass lambda to the constructor - this lambda is valid at the point you are calling the constructor, although it is not valid to call it yet, because it depends on the callback variable set. Then you immediately set the callback variable to the implementation you need, which can now safely reference timer .

If you use a timer that starts in the background thread, then there is a possible race condition where the timer can fire before initializing your callback variable to a non-zero value; hence null check inside lambda. For timers that fire in the user interface thread, this race condition does not exist, and a null check may be omitted.

0
source

Source: https://habr.com/ru/post/957114/


All Articles