Bug in WeakAction in case of Closure Action - c#

In one of the projects I take part in there is a vast use of WeakAction. That's a class that allows to keep reference to an action instance without causing its target not be garbage collected. The way it works is simple, it takes an action on the constructor, and keeps a weak reference to the action's target and to the Method but discards the reference to the action itself. When the time comes to execute the action, it checks if the target is still alive, and if so, invokes the method on the target.
It all works well except for one case - when the action is instantiated in a closure. consider the following example:
public class A
{
WeakAction action = null;
private void _register(string msg)
{
action = new WeakAction(() =>
{
MessageBox.Show(msg);
}
}
}
Since the lambda expression is using the msg local variable, the C# compiler auto generates a nested class to hold all the closure variables. The target of the action is an instance of the nested class instead of the instance of A. The action passed to the WeakAction constructor is not referenced once the constructor is done and so the garbage collector can dispose of it instantly. Later, if the WeakAction is executed, it will not work because the target is not alive anymore, even though the original instance of A is alive.
Now I can't change the way the WeakAction is called, (since it's in wide use), but I can change its implementation. I was thinking about trying to find a way to get access to the instance of A and to force the instance of the nested class to remain alive while the instance of A is still alive, but I don't know how to do get it.
There are a lot of questions about what A has to do with anything, and suggestions to change the way A creates a weak action (which we can't do) so here is a clarification:
An instance of class A wants an instance of class B to notify it when something happens, so it provides a callback using an Action object. A is not aware that B uses weak actions, it simply provides an Action to serve as callback. The fact that B uses WeakAction is an implementation detail that is not exposed. B needs to store this action and use it when needed. But B may live much longer then A, and holding a strong reference to a normal Action (which by itself holds a strong reference of the instance of A that generated it) causes A to never be garbage collected. If A is a part of a list of items that are no longer alive, we expect A to be garbage collected, and because of the reference that B holds of the Action, which by itself points to A, we have a memory leak.
So instead of B holding an Action that A provided, B wraps it into a WeakAction and stores the weak action only. When the time comes to call it, B only does so if theWeakAction is still alive, which it should be as long as A is still alive.
A creates that action inside a method and does not keep a reference to it on his own - that's a given. Since the Action was constructed in the context of a specific instance of A, that instance is the target of A, and when A dies, all weak references to it become null so B knows not to call it and disposes of the WeakAction object.
But sometimes the method that generated the Action uses variables defined locally in that function. In which case the context in which the action runs include not just the instance of A, but also the state of the local variables inside of the method (that is called a "closure"). The C# compiler does that by creating a hidden nested class to hold these variables (lets call it A__closure) and the instance that becomes the target of the Action is an instance of A__closure, not of A. This is something that the user should not be aware of. Except that this instance of A__closure is only referenced by the Action object. And since we create a weak reference to the target, and do not hold a reference to the action, there is no reference to the A__closure instance, and the garbage collector may (and usually does) dispose of it instantly. So A lives, A__closure dies, and despite the fact that A is still expecting the callback to be invoked, B can not do it.
That's the bug.
My question was if somebody knows of a way that the WeakAction constructor, the only piece of code that actually holds the original Action object, temporarily, can in some magic way extract the original instance of A from the A__closure instance that it finds in the Target of the Action. If so, I could perhaps extend A__Closure life cycle to match that of A.

After some more research and after collecting all the useful bits of information from the answers that were posted here, I realized that there is not going to be an elegant and sealed solution to the problem. Since this is a real life problem we went with the pragmatic approach, trying to at least reduce it by handling as many scenarios as possible, so I wanted to post what we did.
Deeper investigation of the Action object that is passed to the constructor of the WeakEvent, and especially the Action.Target property, showed that there are effectively 2 different cases of closure objects.
The first case is when the Lambda uses local variables from the scope of the calling function, but does not use any information from the instance of the A class. In the following example, assume EventAggregator.Register is a method that takes an action and stores a WeakAction that wraps it.
public class A
{
public void Listen(int num)
{
EventAggregator.Register<SomeEvent>(_createListenAction(num));
}
public Action _createListenAction(int num)
{
return new Action(() =>
{
if (num > 10) MessageBox.Show("This is a large number");
});
}
}
The lambda created here uses the num variable, which is a local variable defined in the scope of the _createListenAction function. So the compiler has to wrap it with a closure class in order maintain the closure variables. However, since the lambda does not access any of class A members, there is no need to store a reference to A. The target of the action will therefore not include any reference to the A instance and there is absolutely no way for the WeakAction constructor to reach it.
The second case is illustrated in the following example:
public class A
{
int _num = 10;
public void Listen()
{
EventAggregator.Register<SomeEvent>(_createListenAction());
}
public Action _createListenAction()
{
return new Action(() =>
{
if (_num > 10) MessageBox.Show("This is a large number");
});
}
}
Now _num is not provided as parameter to the function, it comes from the class A instance. Using reflection to learn about the structure of the Target object reveals that the last field the the compiler defines holds a reference to the A class instance. This case also applies when the lambda contains calls to member methods, as in the following example:
public class A
{
private void _privateMethod()
{
// do something here
}
public void Listen()
{
EventAggregator.Register<SomeEvent>(_createListenAction());
}
public Action _createListenAction()
{
return new Action(() =>
{
_privateMethod();
});
}
}
_privateMethod is a member function, so it is called in the context of the class A instance, so the closure must keep a reference to it in order to invoke the lambda in the right context.
So the first case is a Closure that only contains functions local variable, the second contains reference to the parent A instance. In both cases, there are no hard references to the Closure instance, so if the WeakAction constructor just leaves things the way they are, the WeakAction will "die" instantly despite the fact that the class A instance is still alive.
We are faced here with 3 different problems:
How to identify that the target of the action is a nested closure
class instance, and not the original A instance?
How to obtain a reference to the original class A instance?
How to extend the life span of the closure instance so that it lives
as long as the A instance live, but not beyond that?
The answer to the first question is that we rely on 3 characteristics of the closure instance:
- It is private (to be more accurate, it is not "Visible". When using C# compiler, the reflected type has IsPrivate set to true but with VB it does not. In all cases, the IsVisible property is false).
- It is nested.
- As #DarkFalcon mentioned in his answer, It is decorated with the [CompilerGenerated] attribute.
private static bool _isClosure(Action a)
{
var typ = a.Target.GetType();
var isInvisible = !typ.IsVisible;
var isCompilerGenerated = Attribute.IsDefined(typ, typeof(CompilerGeneratedAttribute));
var isNested = typ.IsNested && typ.MemberType == MemberTypes.NestedType;
return isNested && isCompilerGenerated && isInvisible;
}
While this is not a 100% sealed predicate (a malicious programmer may generate a nested private class and decorate it with the CompilerGenerated attribute), in real life scenarios this is accurate enough, and again, we are building a pragmatic solution, not an academic one.
So problem number 1 is solved. The weak action constructor identifies situations where the action target is a closure and responds to that.
Problem 3 is also easily solvable. As #usr wrote in his answer, once we get a hold of the A class instance, adding a ConditionalWeakTable with a single entry where the A class instance is the key and the closure instance is the target, solves the problem. The garbage collector knows not to collect the closure instance as long as the A class instance lives. So that's ok.
The only non - solvable problem is the second one, how to obtain a reference to the class A instance? As I said, there are 2 cases of closures. One where the compiler creates a member that holds this instance, and one where it doesn't. In the second case, there is simply no way to get it, so the only thing we can do is to create a hard reference to the closure instance in order to save it from being instantly garbage collected. This means that it may out live the class A instance (in fact it will live as long as the WeakAction instance lives, which may be forever). But this is not such a terrible case after all. The closure class in this case only contains a few local variables, and in 99.9% of the cases it is a very small structure. While this is still a memory leak, it is not a substantial one.
But just in order to allow users to avoid even that memory leak, we have now added an additional constructor to the WeakAction class, as follows:
public WeakAction(object target, Action action) {...}
And when this constructor is called, we add a ConditionalWeakTable entry where the target is the key and the actions target is the value. We also hold a weak reference to both the target and the actions target and if any of them die, we clear both. So that the actions target lives no less and no more than the provided target. This basically allows the user of the WeakAction to tell it to hold on to the closure instance as long as the target lives. So new users will be told to use it in order to avoid memory leaks. But in existing projects, where this new constructor is not used, this at least minimizes the memory leaks to closures that have no reference to the class A instance.
The case of closures that reference the parent are more problematic because they affect garbase collection. If we hold a hard reference to the closure, we cause a much more drastic memory leak becuase the class A instance will also never be cleared. But this case is also easier to treat. Since the compiler adds a last member that holds a reference to the class A instance, we simply use reflection to extract it and do exactly what we do when the user provides it in the constructor. We identify this case when the last member of the closure instance is of the same type as the declaring type of the closure nested class. (Again, its not 100% accurate, but for real life cases its close enough).
To summarize, the solution I presented here is not 100% sealed solution, simply because there does not seem to be a such solution. But since we have to provide SOME answer to this annoying bug, this solution at least reduces the problem substantially.

You want to extend the lifetime of the closure class instance to be exactly the same that the A instance has. The CLR has a special GC handle type for that: the Ephemeron, implemented as internal struct DependentHandle.
This struct is only exposed as part of the ConditionalWeakTable class. You could create one such table per WeakAction with exactly one item in it. The key would be an instance of A, the value would be the closure class instance.
Alternatively, you could pry open DependentHandle using private reflection.
Or, you could use one globally shared ConditionalWeakTable instance. It probably requires synchronization to use. Look at the docs.
Consider opening a connect issue to make DependentHandle public and link to this question to provide a use case.

a.Target provides access to the object which holds the lambda parameters. Performing a GetType on this will return the compiler-generated type. One option would be to check this type for the custom attribute System.Runtime.CompilerServices.CompilerGeneratedAttribute and keep a strong reference to the object in this case.
Now I can't change the way the WeakAction is called, (since it's in wide use) but I can change it's implementation. Note that this is the only way so far which can keep it alive without requiring modifications to how the WeakAction is constructed. It also doesn't attain the goal of keeping the lambda alive as long as the A object (it would keep it alive as long as the WeakAction instead). I do not believe that is going to be attainable without changing how the WeakAction is constructed as is done in the other answers. At a minimum, the WeakAction needs to obtain a reference to the A object, which you currently do not provide.

Related

How to delete an instance of a class within that instance

class Foo
{
int Data;
public Foo(int data)
{
Data = data;
}
public void Diminish()
{
this.Data--;
}
}
Foo Foo1 = new Foo(5);
Foo1.Diminish();
Is there Any way delete Foo1 When Data is 0 within Foo1?
(Free up the memory used by that instance)
An object never decides when to "delete" or garbage collect itself. It can't know what else has a reference to it. When there are no references left to an object then it gets garbage collected.
In the scenario described in the question - suppose you had a List<Foo>() and then one instance of Foo spontaneously decided, based on its own internal logic, that it should no longer exist? I don't know what that would even look like - all references to it suddenly become null?
If that were possible, the defects it would cause would be beyond all mortal comprehension. We have enough trouble when two threads modify the same object or property because of improper/nonexistent locking, and so from the viewpoint of one thread something has changed when it shouldn't.
But this would create similar scenarios even in single-threaded applications.
var myFoo = new Foo(1);
myFoo.Diminish();
var x = myFoo.Data; // Null reference exception! WTH - it nullified itself!
A fundamental concept of OOP is that classes don't need to know too much about the internal state of other classes. But now you'd need to "know" that a class could suddenly choose to opt-out and jump off the cliff into the GC, and constantly null-check objects that already weren't null.
It's a very good thing that this is not possible.
As some comments and at least one answer explain, a normal reference in a .NET program is exactly what keeps that object alive. It is not possible to "delete" the object explicitly in the way you seem to mean.
An object will remain alive as long as it is "reachable" (i.e. it is possible to start with a "rooted" reference and find the reference to that object). Once it is no longer reachable (which could happen even when a reference to the object exists, as long as that reference can't be reached from a rooted reference…e.g. a circular reference between two objects does not prevent garbage collection of those two objects, as long as the only reference to each is from the other).
All of this is discussed in detail in documentation references provided elsewhere.
Now, all that said, depending on what you're actually trying to do, you might find that the WeakReference<T> class is what you need. This class provides a way to reference objects without preventing them from being garbage-collected. You can combine this with reference-counting and an object repository to maintain non-weak references to keep the object alive to produce the results you want.
For example:
class RefCounted
{
private static readonly HashSet<RefCounted> _alive = new HashSet<RefCounted>();
private int _referenceCount;
public RefCounted()
{
AddRef();
}
public AddRef()
{
if (_referenceCount == 0)
{
// the collection ensures a strong reference to the object, to prevent
// the object from being garbage-collected even if the only other
// references are weak references.
_alive.Add(this);
}
_referenceCount++;
}
public Release()
{
if (--_referenceCount)
{
// no longer need to force the object to stay alive; if the only remaining
// references to the object are weak references, then the garbage
// collector may collect the object.
_alive.Remove(this);
}
}
}
Then you can use it like this:
WeakReference<RefCounted> o = new WeakReference<RefCounted>(new RefCounted());
RefCounted r;
if (o.TryGetTarget(out r))
{
// do stuff with r
// done with the object? Then call:
r.Release();
}
It is important to note that this is not precisely what you're asking for. The above still does not give you deterministic control over the actual garbage collection of the object. Furthermore, once you retrieve the target of the weak reference into e.g. a local variable, you now hold a strong reference to the object for the lifetime of that local variable (i.e. up to the last point that variable is used) and the object cannot be collected during that lifetime, even if the reference count goes to 0 (though at that point the object will of course be collected once that variable no longer exists or is reachable).
But if you really have a scenario where you need to use some type of reference counting to track and in some way control the lifetime of an object, the above is how you'd do it. You would have to store the WeakReference<T> objects rather than direct references to T, retrieving the target reference at each place in the code where you need it.
Of course, there remains the question of why do you think you need reference counting. And even if you need reference counting, why should the reference count be directly related to the lifetime of the object? Your question does not explain these aspects, and so I can't really address them directly. I will say that reference counting is not needed at all for effective management of managed objects in .NET programs. As long as you don't actively interfere with the maintenance and use of references to managed objects, the .NET garbage collection system will do a far better job of dealing with the lifetimes of objects than you or I could.
An object instance will remain "alive" as long as any piece of code references it. So, it's something you cannot really control from within a class.
From MSDN:
The .NET Framework's garbage collector manages the allocation and release of memory for your application. Each time you create a new object, the common language runtime allocates memory for the object from the managed heap. As long as address space is available in the managed heap, the runtime continues to allocate space for new objects. However, memory is not infinite. Eventually the garbage collector must perform a collection in order to free some memory. The garbage collector's optimizing engine determines the best time to perform a collection, based upon the allocations being made. When the garbage collector performs a collection, it checks for objects in the managed heap that are no longer being used by the application and performs the necessary operations to reclaim their memory.
So the garbage collector will take care of releasing the object when it goes out of scope unless you need to release some expensive (memory consumption) resource. You can sets the reference of the instance to null, this will terminate the object, unless there is some other reference to the object:
Foo Foo1 = new Foo(5);
Foo1.Diminish();
if(Foo1.Data == 0)
{
Foo1 = null;
}
C# is a managed language, you can't "delete" anything, once something can no longer be referenced the value clean up will be handled by the Garbage Collector.
There are edge cases where you need to handle clean up, or be more aware of how you are handling creation:
Instances of classes that implement IDisposable interface should be disposed of by calling the Dispose() method or wrapped in a using statement
Unmanaged resource wrappers, such as IntPtr may need to be cleaned up using Marshal.FreeHGlobal(IntPtr) or Marshal.FreeCoTaskMem(IntPtr) if you are required to manage them and you're making a call to unmanaged code
Static (and possibly long living) values will never be un-referenceable unless you actively set variables to null or remove items from collections

C# Actions and GC

I am using Actions in C# and I was wondering if I need to set the instance of Action to null once I wish the GC to collect the objects properly? Here is an example:
public class A
{
public Action a;
}
public class B
{
public string str;
}
public class C
{
public void DoSomething()
{
A aClass = new A();
B bClass = new B();
aClass.a = () => { bClass.str = "Hello"; }
}
}
Inside my Main method I have something like this:
public void Main(...)
{
C cClass = new C();
cClass.DoSomething();
Console.WriteLine("At this point I dont need object A or B anymore so I would like the GC to collect them automatically.");
Console.WriteLine("Therefore I am giving GC time by letting my app sleep");
Thread.Sleep(3000000);
Console.WriteLine("The app was propably sleeping long enough for GC to have tried collecting objects at least once but I am not sure if A and B objects have really been collected");
}
}
Please read the Console.WriteLine text it will help you understand what I am asking here.
If I apply my understanding of GC to this example the GC would never collect the objects since A cannot be destroyed because it holds instance of B. Am I right?
How can I properly collect those two objects? Do I need to set instances of Actions to null just to let GC collect objects before the end of application or is there already some kind of very smart mechanism by GC that knows how to destroy objects who have Actions such as A and B are?
EDIT: The question is about GC and collecting objects properly. Its not about calling the method collect().
There are numerous problems with this question. Rather than answer your question directly I'm going to answer the questions you should be asking.
Let's first disabuse you of your notions about the GC.
Will sleeping for a long time activate the garbage collector?
No.
What activates the garbage collector?
For testing purposes you can use GC.Collect() and GC.WaitForPendingFinalizers(). Use these only for testing purposes; it is a bad practice to use them in production code except in some very rare circumstances.
Under normal situations the things that trigger a GC are complicated; the GC is a highly tuned piece of machinery.
What are the semantics of garbage collection insofar as closed-over outer variables are concerned?
The lifetime of a closed-over outer variable of a lambda that is converted to a delegate is extended to be not shorter than the lifetime of the delegate.
Suppose I have a variable of type Action which is initialized with a lambda that is closed over an outer local variable of reference type. In order to make the object referred to by that variable eligable for collection, do I have to set the variable of type Action to null?
In the vast majority of cases, no. The garbage collector is very smart; just let it do its work and do not worry about it. Eventually the runtime will determine that the Action variable cannot be reached by any live root and will make it eligable for collection; the closed-over outer variable will then become eligible.
There may be extremely rare situations in which you want to throw away references to the Action sooner, but they are rare; the vast majority of time, just let the GC do its job without interference.
Are there situations in which outer variables can have their lifetimes extended too long?
Yes. Consider:
void M()
{
Expensive e = new Expensive();
Cheap c = new Cheap();
Q.longLived = ()=>c; // static field
Q.shortLived = ()=>e; // static field
}
When M() is executed a closure is created for both delegates. Suppose that shortLived is going to be set to null soon, and longLived is set to null far in the future. Unfortunately both local variables have their lifetimes extended to the lifetime of the object referred to by longLived, even though only c is still reachable. The expensive resource e is not released until the reference in longLived is dead.
Numerous programming languages have this problem; some implementations of JavaScript, Visual Basic, and C# all have this problem. There is some talk of fixing it in the Roslyn release of C# / VB but I do not know if that will come to fruition.
In that case the solution is to avoid the situation in the first place; don't make two lambdas that share a closure if one of the delegates will live much longer than the other.
Under what circumstances does a local that is not a closed-over outer variable become eligable for collection?
The moment that the runtime can prove that a local cannot be read from again, the thing it references becomes eligable for collection (assuming the local is the only root of course.) In your example program there is no requirement that the references in aClass and bClass remain alive until the end of the method. In fact, there are rare but possible circumstances in which the GC can be deallocating an object on one thread while it is still in its constructor on another thread! The GC can be very aggressive about determining what is dead, so be careful.
How do I keep something alive in the face of an aggressive GC?
GC.KeepAlive() of course.
I am using Actions in C# and I was wondering if I need to set the instance of Action to null once I wish the GC to collect the objects properly?
Not necessarily. As long as no object is reachable which references the delegate, the delegate will be eligible for GC.
That being said, in your example, aClass and bClass are still valid variables, and refer to reachable objects. This means that aClass.a is still reachable, and not eligible for GC, so it will not be collected.
If you wanted these to be garbage collected, you would need to explicitly set the object reference (aClass) to null so that the A instance, and it's contained delegate, were no longer reachable objects, and then you'd have to explicitly call GC.Collect to trigger the GC, since nothing will cause the GC to trigger in your code.

Why anonymous methods inside structs can not access instance members of 'this'

I have a code like the following:
struct A
{
void SomeMethod()
{
var items = Enumerable.Range(0, 10).Where(i => i == _field);
}
int _field;
}
... and then i get the following compiler error:
Anonymous methods inside structs can not access instance members of 'this'.
Can anybody explains what's going on here.
Variables are captured by reference (even if they were actually value-types; boxing is done then).
However, this in a ValueType (struct) cannot be boxed, and hence you cannot capture it.
Eric Lippert has a nice article on the surprises of capturing ValueTypes. Let me find the link
The Truth About Value Types
Note in response to the comment by Chris Sinclair:
As a quick fix, you can store the struct in a local variable: A thisA = this; var items = Enumerable.Range(0, 10).Where(i => i == thisA._field); – Chris Sinclair 4 mins ago
Beware of the fact that this creates surprising situations: the identity of thisA is not the same as this. More explicitly, if you choose to keep the lambda around longer, it will have the boxed copy thisA captured by reference, and not the actual instance that SomeMethod was called on.
When you have an anonymous method it will be compiled into a new class, that class will have one method (the one you define). It will also have a reference to each variable that you used that was outside of the scope of the anonymous method. It's important to emphasize that it is a reference, not a copy, of that variable. "lambdas close over variables, not values" as the saying goes. This means that if you close over a variable outside of the scope of a lambda, and then change that variable after defining the anonymous method (but before invoking it) then you will see the changed value when you do invoke it).
So, what's the point of all of that. Well, if you were to close over this for a struct, which is a value type, it's possible for the lambda to outlive the struct. The anonymous method will be in a class, not a struct, so it will go on the heap, live as long as it needs to, and you are free to pass a reference to that class (directly or indirectly) wherever you want.
Now imagine that we have a local variable, with a struct of the type you've defined here. We use this named method to generate a lambda, and let's assume for a moment that the query items is returned (instead of the method being void). Would could then store that query in another instance (instead of local) variable, and iterate over that query some time later on another method. What would happen here? In essence, we would have held onto a reference to a value type that was on the stack once it is no longer in scope.
What does that mean? The answer is, we have no idea. (Please look over the link; it's kinda the crux of my argument.) The data could just happen to be the same, it could have been zeroed out, it could have been filled by entirely different objects, there is no way of knowing. C# goes to great lengths, as a language, to prevent you from doing things like this. Languages such as C or C++ don't try so hard to stop you from shooting your own foot.
Now, in this particular case, it's possible that you aren't going to use the lambda outside of the scope of what this refers to, but the compiler doesn't know that, and if it lets you create the lambda it has no way of determining whether or not you expose it in a way that could result in it outliving this, so the only way to prevent this problem is to disallow some cases that aren't actually problematic.

Do delegates create thread safety issues with local variables?

I am initializing a mutable class instance as a local variable with new keyword. Then I pass this object as a parameteter to a delegate. Is this variable's lifetime extended by the delegate? Do other threads use this variable or create their own instances? I may be asking the obvious but I want to be sure.
public void DoSometing(Action<Foo> action)
{
Foo foo = new Foo();
action.Invoke(foo);
}
Whenever you pass local variables that "escape" the method one way or another, you do extend its lifetime. In C# you will never operate upon a variable that contains a reference to a non-existant object -- the concept makes no sense in a managed environment.
So yes, foo will continue to live on, and you will need to be concerned with thread-safety in exactly the same way as if you simply called another ordinary method. In this scenario, lambdas do not change the complexion of the problem.
However, sometimes this can be more subtle, especially if you return a lambda -- one which closes over local variables. In such a scenario, all the variables you reference from within the lambda live on in the same way as foo.

What happens when you create an instance of an object containing no state in C#?

I am I think ok at algorithmic programming, if that is the right term? I used to play with turbo pascal and 8086 assembly language back in the 1980s as a hobby. But only very small projects and I haven't really done any programming in the 20ish years since then. So I am struggling for understanding like a drowning swimmer.
So maybe this is a very niave question or I'm just making no sense at all, but say I have an object kind of like this:
class Something : IDoer
{
void Do(ISomethingElse x)
{
x.DoWhatEverYouWant(42);
}
}
And then I do
var Thing1 = new Something();
var Thing2 = new Something();
Thing1.Do(blah);
Thing2.Do(blah);
does Thing1 = Thing2? does "new Something()" create anything? Or is it not much different different from having a static class, except I can pass it around and swap it out etc.
Is the "Do" procedure in the same location in memory for both the Thing1(blah) and Thing2(blah) objects? I mean when executing it, does it mean there are two Something.Do procedures or just one?
They are two separate objects; they just don't have state.
Consider this code:
var obj1 = new object();
var obj2 = new object();
Console.WriteLine(object.ReferenceEquals(obj1, obj2));
It will output False.
Just because an object has no state doesn't mean it doesn't get allocated just like any other object. It just takes very little space (just like an object).
In response to the last part of your question: there is only one Do method. Methods are not stored per instance but rather per class. If you think about it, it would be extremely wasteful to store them per instance. Every method call to Do on a Something object is really the same set of instructions; all that differs between calls from different objects is the state of the underlying object (if the Something class had any state to begin with, that is).
What this means is that instance methods on class objects are really behaviorally the same as static methods.
You might think of it as if all instance-level methods were secretly translated as follows (I'm not saying this is strictly true, just that you could think of it this way and it does kind of make sense):
// appears to be instance-specific, so you might think
// it would be stored for every instance
public void Do() {
Do(this);
}
// is clearly static, so it is much clearer it only needs
// to be stored in one place
private static Do(Something instance) {
// do whatever Do does
}
Interesting side note: the above hypothetical "translation" explains pretty much exactly how extension methods work: they are static methods, but by qualifying their first parameter with the this keyword, they suddenly look like instance methods.
There are most definitely two different objects in memory. Each object will consume 8 bytes on the heap (at least on 32-bit systems); 4 for the syncblock and 4 for the type handle (which includes the method table). Other than the system-defined state data there is no other user-defined state data in your case.
There is a single instance of the code for the Something.Do method. The type handle pointer that each object holds is how the CLR locates the different methods for the class. So even though there are two different objects in memory they both execute the same code. Since Something.Do was declared as an instance method it will have a this pointer passed to it internally so that the code can modify the correct instance members depending on which object was invoking the method. In your case the Something class has no instance members (and thus no user-defined state) and so this is quite irrelevant, but still happens nevertheless.
No they are not the same. They are two separate instances of the class Something. They happen to be identically instantiated, that is all.
You would create 2 "empty" objects, there would be a small allocation on the heap for each object.
But the "Do" method is always in the same place, that has nothing to do with the absence of state. Code is not stored 'in' a class/object. There is only 1 piece of code corresponding to Do() and it has a 'hidden' parameter this that points to the instance of Something it was called on.
Conceptually, Thing1 and Thing2 are different objects, but there is only one Something.Do procedure.
The .Net runtime allocates a little bit of memory to each of the objects you create - one chunk to Thing1 and another to Thing2. The purpose of this chunk of memory is to store (1) the state of the object and (2) a the address of any procedures that that belong to the object. I know you don't have any state, but the runtime doesn't care - it still keeps two separate references to two separate chunks of memory.
Now, your "Do" method is the same for both Thing1 and Thing2, do the runtime only keeps one version of the procedure in memory.
he memory allocated Thing1 includes the address of the the Do method. When you invoke the Do method on Thing1, it looks up the address of its Do method for Thing1 and runs the method. The same thing happens with the other object, Thing2. Although the objects are different, the same Do method is called for both Thing1 and Thing2.
What this boils down to is that Thing1 and Thing2 are different, in that the names "Thing1" and "Thing2" refer to different areas of memory. The contents of this memory is he same in both cases - a single address that points to the "Do" method.
Well, that's the theory, anyway. Under the hood, there might be some kind of optimisation going on (See http://www.wrox.com/WileyCDA/Section/CLR-Method-Call-Internals.id-291453.html if you're interested), but for most practical purposes, what I have said is the way things work.
Thing1 != Thing2
These are two different objects in memory.
The Do method code is in the same place for both objects. There is no need to store two different copies of the method.
Each reference type (Thing1, Thing2) is pointing to a different physical address in main memory, as they have been instantiated separately. The thing pointed to in memory is the bytes used by the object, whether it has a state or not (it always has a state, but whether it has a declared/initialised state).
If you assigned a reference type to another reference type (Thing2 = Thing1;) then it would be the same portion of memory used by two different reference types, and no new instantiation would take place.
A good way of think of the new constructor(), is that you are really just calling the method inside your class whos sole responsibility is to produce you a new instance of an object that is cookie cutted from your class.
so now you can have multiple instances of the same class running around at runtime handling all sorts of situations :D
as far as the CLR, you are getting infact 2 seperate instances on memory that each contain pointers to it, it is very similar to any other OOP language but we do not have to actually interact with the pointers, they are translated the same as a non reference type, so we dont have to worry about them!
(there are pointers in C# if you wish to whip out your [unsafe] keyword!)

Categories

Resources