112
votes

I was hoping to do something like this, but it appears to be illegal in C#:

public Collection MethodThatFetchesSomething<T>()
    where T : SomeBaseClass
{
    return T.StaticMethodOnSomeBaseClassThatReturnsCollection();
}

I get a compile-time error:

'T' is a 'type parameter', which is not valid in the given context.

Given a generic type parameter, how can I call a static method on the generic class? The static method has to be available, given the constraint.

9
The links in the comment from @EricLippert above are no longer valid. The articles can now be found here, here and here. - Bill Tür
@BillTür Thanks! I will eventually move those over to ericlippert.com but it is a slow process. - Eric Lippert

9 Answers

63
votes

In this case you should just call the static method on the constrainted type directly. C# (and the CLR) do not support virtual static methods. So:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

...can be no different than:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

Going through the generic type parameter is an unneeded indirection and hence not supported.

31
votes

To elaborate on a previous answer, I think reflection is closer to what you want here. I could give 1001 reasons why you should or should not do something, I'll just answer your question as asked. I think you should call the GetMethod method on the type of the generic parameter and go from there. For example, for a function:

public void doSomething<T>() where T : someParent
{
    List<T> items=(List<T>)typeof(T).GetMethod("fetchAll").Invoke(null,new object[]{});
    //do something with items
}

Where T is any class that has the static method fetchAll().

Yes, I'm aware this is horrifically slow and may crash if someParent doesn't force all of its child classes to implement fetchAll but it answers the question as asked.

8
votes

The only way of calling such a method would be via reflection, However, it sounds like it might be possible to wrap that functionality in an interface and use an instance-based IoC / factory / etc pattern.

5
votes

It sounds like you're trying to use generics to work around the fact that there are no "virtual static methods" in C#.

Unfortunately, that's not gonna work.

3
votes

I just wanted to throw it out there that sometimes delegates solve these problems, depending on context.

If you need to call the static method as some kind of a factory or initialization method, then you could declare a delegate and pass the static method to the relevant generic factory or whatever it is that needs this "generic class with this static method".

For example:

class Factory<TProduct> where TProduct : new()
{
    public delegate void ProductInitializationMethod(TProduct newProduct);


    private ProductInitializationMethod m_ProductInitializationMethod;


    public Factory(ProductInitializationMethod p_ProductInitializationMethod)
    {
        m_ProductInitializationMethod = p_ProductInitializationMethod;
    }

    public TProduct CreateProduct()
    {
        var prod = new TProduct();
        m_ProductInitializationMethod(prod);
        return prod;
    }
}

class ProductA
{
    public static void InitializeProduct(ProductA newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class ProductB
{
    public static void InitializeProduct(ProductB newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class GenericAndDelegateTest
{
    public static void Main()
    {
        var factoryA = new Factory<ProductA>(ProductA.InitializeProduct);
        var factoryB = new Factory<ProductB>(ProductB.InitializeProduct);

        ProductA prodA = factoryA.CreateProduct();
        ProductB prodB = factoryB.CreateProduct();
    }
}

Unfortunately you can't enforce that the class has the right method, but you can at least compile-time-enforce that the resulting factory method has everything it expects (i.e an initialization method with exactly the right signature). This is better than a run time reflection exception.

This approach also has some benefits, i.e you can reuse init methods, have them be instance methods, etc.

2
votes

As of now, you can't. You need a way of telling the compiler that T has that method, and presently, there's no way to do that. (Many are pushing Microsoft to expand what can be specified in a generic constraint, so maybe this will be possible in the future).

2
votes

Here, i post an example that work, it's a workaround

public interface eInterface {
    void MethodOnSomeBaseClassThatReturnsCollection();
}

public T:SomeBaseClass, eInterface {

   public void MethodOnSomeBaseClassThatReturnsCollection() 
   { StaticMethodOnSomeBaseClassThatReturnsCollection() }

}

public Collection MethodThatFetchesSomething<T>() where T : SomeBaseClass, eInterface
{ 
   return ((eInterface)(new T()).StaticMethodOnSomeBaseClassThatReturnsCollection();
}
2
votes

You should be able to do this using reflection, as is described here

Due to link being dead, I found the relevant details in the wayback machine:

Assume you have a class with a static generic method:

class ClassWithGenericStaticMethod
{
    public static void PrintName<T>(string prefix) where T : class
    {
        Console.WriteLine(prefix + " " + typeof(T).FullName);
    }
}

How can you invoke this method using relection?

It turns out to be very easy… This is how you Invoke a Static Generic Method using Reflection:

// Grabbing the type that has the static generic method
Type typeofClassWithGenericStaticMethod = typeof(ClassWithGenericStaticMethod);

// Grabbing the specific static method
MethodInfo methodInfo = typeofClassWithGenericStaticMethod.GetMethod("PrintName", System.Reflection.BindingFlags.Static | BindingFlags.Public);

// Binding the method info to generic arguments
Type[] genericArguments = new Type[] { typeof(Program) };
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);

// Simply invoking the method and passing parameters
// The null parameter is the object to call the method from. Since the method is
// static, pass null.
object returnValue = genericMethodInfo.Invoke(null, new object[] { "hello" });
0
votes

You can do what I call a surrogate singleton, I've been using it as a sort of "static inheritance" for a while

interface IFoo<T> where T : IFoo<T>, new()
{
    ICollection<T> ReturnsCollection();
}

static class Foo<T> where T : IFoo<T>, new()
{
    private static readonly T value = new();
    public static ICollection<T> ReturnsCollection() => value.ReturnsCollection();
}

// Use case

public ICollection<T> DoSomething<T>() where T : IFoo<T>, new()
{
    return Foo<T>.ReturnsCollection();
}