views:

128

answers:

6

Hi!

I have a generic class:

public class MyList<LinkedItem> : List<LinkedItem> where LinkedItem : MyItem, new()
{
}

From that generic class, I would like to access a static function from the LinkedItem Class which is a descendant of MyItem class. (thus without creating an instance of the LinkedItem).

Is it possible?

Thank you,

Eric

+2  A: 

No this is not possible directly from the type parameter because you cannot invoke static methods on generic type parameters (C# Lang Spec section 4.5).

A type parameter cannot be used in a member access (§7.5.4) or type name (§3.8) to identify a static member or a nested type.

Yes this is possible to achieve via reflection tricks as other people noted. But generally speaking using reflection to solve a simple method invocation scenario is an indication of bad design.

A much better design would be to pass a factory / delegate around which encapsulates the static method in a type safe manner.

class MyItem : MyItem {
  static void TheFunction() { ... }
}

public class MyList<LinkedItem> : List<LinkedItem> where LinkedItem : MyItem, new()
{
  public MyList(Action theStaticFunction) {
    ...
  }
}

new MyList<MyItem>(MyItem.TheFunction);
JaredPar
@Downvoter, care to explain?
JaredPar
You say, "No this is not possible". There are three answers demonstrating that it is possible, and how to do it. How are you arguing against the down-vote, that your answer isn't misleading?
Jon Hanna
@Jon, my answer is correct even in the face of the other answers. Using reflection is not invoking the method on the generic type parameter, it is invoking it on the type of the generic type parameter. There is a substantial difference between those two scenarios.
JaredPar
@Jon, updated to include relevant C# lang spec reference.
JaredPar
A: 

This isn't possible. There's no way to declare a constraint on the LinkedItem parameter that says that it must contain the static method in question.

Possibly the closest you'll get is:

public class ILinkedItemFactory<T>
{
    void YourMethodGoesHere();
}

public class MyList<LinkedItem, Factory> : List<LinkedItem>
    where Factory : ILinkedItemFactory<LinkedItem>
    where LinkedItem : MyItem, new()
{
    public MyList(Factory factory)
    {
        factory.YourMethodGoesHere();
    }
}
Tim Robinson
@Downvoter, care to explain?
Tim Robinson
+1 I don't know why you've been downvoted, or by whom, but I've evened it out again :)
Abel
+2  A: 

It can be done through reflection. There's no straight forward way to do it since C# has no API constraints on static memebers.

I am not sure what is the scenario you're in, but in most cases this is not a recommended solution :)

public class MyList<LinkedItem> : List<LinkedItem> 
                                      where LinkedItem : MyItem, new()
{
    public int CallStaticMethod()
    {
        // Getting a static method named "M" from runtime type of LinkedItem 
        var methodInfo = typeof(LinkedItem)
                      .GetMethod("M", BindingFlags.Static | BindingFlags.Public);

        // Invoking the static method, if the actual method will expect arguments
        // they'll be passed in the array instead of empty array
        return (int) methodInfo.Invoke(null, new object[0]);
    }

}

public class MyItem
{
}

class MyItemImpl : MyItem
{
    public MyItemImpl()
    {
    }

    public static int M()
    {
        return 100;
    }
}

So, for example the next code will print 100:

public void Test()
{
    Console.WriteLine(new MyList<MyItemImpl>().CallStaticMethod());
}
Elisha
+2  A: 

Yes, it is possible, but you have to use reflection by obtaining a MethodInfo from typeof(T).GetMethod("Foo", BindingFlags.Public | BindingFlags.Static) and then calling Invoke on it.

It can be very useful, particularly if using the same technique on a ConstructorInfo rather than a MethodInfo, to create a generic factory that uses parameters in the constructor. It is though one to use sparingly. In particular, there is no way of guaranteeing at compile time that the type in question has a static method of the required signature, so type-safety is gone and such an error won't be caught until run-time.

Jon Hanna
A: 

This is, by default, not possible. However, if you know the name of the method you want to invoke, and you are positive that every LinkedItem type will contain this method, you can use reflection to reach your goal. Note: there's often a better way than resolving to reflection for general programming tasks.

The following will always output true for DoSomething. It invokes a static member that's always available (I removed your generic type constraint, as that's not important with static methods).

public class MyList<LinkedItem> : List<LinkedItem>
{
    public bool DoSomething()
    {
        Type t = typeof(LinkedItem);
        object o = new Object();
        var result = t.InvokeMember("ReferenceEquals",
            BindingFlags.InvokeMethod |
            BindingFlags.Public |
            BindingFlags.Static,
            null,
            null, new[] { o, o });

        return (result as bool?).Value;
    }
}

// call it like this:
MyList<string> ml = new MyList<string>();
bool value = ml.DoSomething();   // true

PS: meanwhile, while I typed this, others seem to suggest the same approach ;-)

Abel
A: 

This is completely possible though not directly in the way you are stating without maybe reflection. You would want to implement a non-static access method in the baseclass and have it overridden in every specific inheriting class.

public class MyItem
{
    public static void DoSomeStaticStuff() { //DoSomeStaticStuff for MyItem }
    public virtual void AccessSomeStaticStuff() { MyItem.DoSomeStaticStuff(); }
}

public class SomeItem : MyItem
{
    public static void DoSomeStaticStuff() { //DoSomeStaticStuff for SomeItem }
    public override void AccessSomeStaticStuff() { SomeItem.DoSomeStaticStuff(); }
}

Then in your class which has the constraint where T : MyItem you would just call T.AccessSomeStaticStuff();

Jimmy Hoffa
@Jimmy Hoffa, static method cannot be virtual, this won't compile.
Elisha
@Elisha: Sorry wasn't thinking it through, corrected.
Jimmy Hoffa