Reputation: 2901
I have a class that declares a virtual method. However the specific implementations of this method do not explicitly refer to "this" object. They just return a value that is specific for that class.
So one might consistently desire to call this method not only on a specific object but also on the class itself. Since this is of course not possible on the syntactic level, I think it should be at least possible through reflection. That is, I want to iterate through all the classes in my assembly and determine which class returns which value as the response from the said method.
But my naive approach failed with a null reference exception when trying to invoke the method. Why? I expected it to succeed because I have used a concrete class to identify the concrete overridden method, so the "this" object and its virtual method table is not needed to resolve the method.
How can I make it work? (of course excluding the "solution" to define a second truly static method that returns the same value).
using System;
using System.Reflection;
namespace StaticInvoke
{
public abstract class Foo
{
public abstract string StaticValue {get;}
}
public class MyFirstFoo: Foo
{
public override string StaticValue {get {return "A first attempt to foo-ize Foo.";}}
}
class Program
{
public static void Main(string[] args)
{
Type myFirstFooType = typeof(MyFirstFoo);
PropertyInfo myFirstStaticValueProperty = myFirstFooType.GetProperty("StaticValue");
MethodInfo myFirstStaticValueMethod = myFirstStaticValueProperty.GetGetMethod();
string result = (string)myFirstStaticValueMethod.Invoke(null, null);
Console.WriteLine("MyFirstFoo.StaticValue == "+result);
Console.Write("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
Upvotes: 3
Views: 548
Reputation: 40838
This is possible but not without some abuse of the reflection system. I am posting it more for informational purposes because I think it illustrates some interesting parts of how C# and .NET work. However I would caution this is extremely brittle and definitely an off-label use of reflection.
What we need to be able to do here is two fold. One is pass a null receiver to an instance method. This is possible via mechanisms like CreateDelegate which can transform an instance invocation into a static invocation where the first parameter is the instance which this
will refer to (often called the receiver). The interesting thing about .NET is that on a low-level all methods are static; instance methods have the first parameter slot reserved for the this
reference available in the method.
However there is another is issue. You also want to make what is effectively a non-virtual call on a virtual method. The C# compiler exposes this only in very limited scenarios, for example when invoking a method via the base reference. To make a non-virtual call directly the only way is to emit the Call opcode (instead of CallVirt) in manually generated IL.
static void Main(string[] args)
{
Type myFirstFooType = typeof(MyFirstFoo);
// locate the underlying methodinfo, properties have get and set methods.
PropertyInfo myFirstStaticValueMethod = myFirstFooType.GetProperty("StaticValue");
MethodInfo getMethod = myFirstStaticValueMethod.GetGetMethod();
// we need to generate a method call that is non-virtual to a virtual method
// this is normally not allowed in C# because it is prone to error and brittle.
// Here we directly emit IL to do so.
var method = new DynamicMethod("NonVirtualGetter", typeof(string), Type.EmptyTypes, typeof(MyFirstFoo), true);
var ilgen = method.GetILGenerator();
ilgen.Emit(OpCodes.Ldnull); // load a null value for the receiver
ilgen.Emit(OpCodes.Call, getMethod); // invoke the getter method
ilgen.Emit(OpCodes.Ret);
// generate a delgate to the dynamic method.
var getter = (Func<string>)method.CreateDelegate(typeof(Func<string>));
string result = getter();
Console.WriteLine("MyFirstFoo.StaticValue == " + result);
}
Upvotes: 4
Reputation: 2901
I have decided to resolve the problem the other way around. If the information inside StaticValue is static, then I will keep it truly static.
public abstract class Foo
{
public string StaticValue
{
get
{
FieldInfo targetField = GetType().GetField("staticValue");
return (string)targetField.GetValue(null);
}
}
}
public class MyFirstFoo: Foo
{
public static string staticValue = "A first attempt to foo-ize Foo.";
}
public class MySecondFoo: Foo
{
public static string staticValue = "A second attempt to foo-ize Foo.";
}
So when I iterate through all my foo classes in my assembly by reflection, I can simply get the "staticValue" field from each, whereas if I already have a specific Foo object, I might just call the method "StaticValue()".
The ugly thing about this is, of course, that the compiler can't check if a specific Foo class defines the field "staticValue". Making it a static method instead doesn't help either because there is no such thing as a static virtual method or a static interface method in C#. So my solution seems to be the only reasonable one.
Upvotes: -1
Reputation: 54656
However the specific implementations of this method do not explicitly refer to "this" object.
Yes, the c# language does not require prefixing this
because it's not necessary.
So one might consistently desire to call this method not only on a specific object but also on the class itself.
That cannot be done. C# does not allow both an instance method and a static method with the same name. So the desire cannot be solved and another solution is necessary.
Since this is of course not possible on the syntactic level, I think it should be at least possible through reflection.
Reflection only operates on what is allowed. If you can't have a both a static and instance with the same name, Reflection won't solve it.
But my naive approach failed with a null reference exception when trying to invoke the method. Why?
Because you called an Instance Method on null. Your example could be simplified to:
(null as string).Count()
Same thing, without reflection. You cannot call Count() on null.
I expected it to succeed because I have used a concrete class to identify the concrete overridden method, so the "this" object and its virtual method table is not needed to resolve the method.
You still can't call methods on a Null object regardless of how you cast it.
How can I make it work? (of course excluding the "solution" to define a second truly static method that returns the same value).
You cannot based on your requirement of:
call this method not only on a specific object but also on the class itself.
Update 1:
Rather my requirement is to obtain the obviously static information contained in the instance method body (the value) in a static context, whatever the solution may be.
This is really confusing, so here is example code:
public class Person
{
// So this is "Static Inforamtion"
public static int StaticInformation()
{
return 1;
}
// instance method
public static int InstanceMethod()
{
return StaticInformation();
}
}
public static class StaticClass
{
public static int StaticContext()
{
return Person.InstanceMethod();
}
}
This is assumed based on your statement. This is the best I can do to describe your sentence as code. Saying static information should mean to most if not all .Net developers, a Method, Property or Field marked as Static. However, it's extremely ambiguous to state static information contained in the instance body. What is body? Body of the instance method or body of the class?
I'd also wonder WHY anyone would want to do this. It seems like this is just a theoretical problem and wouldn't solve any real world scenarios. Under what situation in C# would I have a method pointer of sorts (MethodInfo) and want to invoke it without knowing if I do or do not have an instance? And if the same result is expected, then why the need for an Instance method at all?
Upvotes: 5