125

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.

Remi Despres-Smyth
  • 4,173
  • 3
  • 36
  • 46
  • 6
    See http://blogs.msdn.com/b/ericlippert/archive/2007/06/14/calling-static-methods-on-type-parameters-is-illegal-part-one.aspx, and http://blogs.msdn.com/b/ericlippert/archive/2007/06/18/calling-static-methods-on-type-parameters-is-illegal-part-two.aspx and http://blogs.msdn.com/b/ericlippert/archive/2007/06/21/3445650.aspx for more on this topic. – Eric Lippert Jul 18 '13 at 23:21
  • 7
    The links in the comment from @EricLippert above are no longer valid. The articles can now be found [here](https://learn.microsoft.com/de-de/archive/blogs/ericlippert/calling-static-methods-on-type-parameters-is-illegal-part-one), [here](https://learn.microsoft.com/de-de/archive/blogs/ericlippert/calling-static-methods-on-type-parameters-is-illegal-part-two) and [here](https://learn.microsoft.com/de-de/archive/blogs/ericlippert/calling-static-methods-on-type-parameters-is-illegal-part-three). – Bill Tür stands with Ukraine Sep 16 '20 at 08:49
  • @BillTür Thanks! I will eventually move those over to ericlippert.com but it is a slow process. – Eric Lippert Sep 16 '20 at 15:42

9 Answers9

69

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.

DavidRR
  • 18,291
  • 25
  • 109
  • 191
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 33
    But what if you masked your static method in a child class? public class SomeChildClass : SomeBaseClass{ public new static StaticMethodOnSomeBaseClassThatReturnsCollection(){} } Could you do something to access that static method from a generic type? – Hugo Migneron Jul 22 '10 at 19:41
  • 2
    Check out Joshua Pech's answer below, I believe that would work in that case. – Remi Despres-Smyth Dec 05 '11 at 20:08
  • 1
    Would `return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();` work? If so you might want to add that to your answer. Thanks. It worked for me. In my case my class had a type param so I did `return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();` and that worked. – toddmo May 18 '16 at 23:03
38

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.

KatDevsGames
  • 1,109
  • 10
  • 21
  • 2
    No, not at all. JaredPar got it absolutely right: T.StaticMethodOnSomeBaseClassThatReturnsCollection where T : SomeBaseClass is no different than simply stating SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection. – Remi Despres-Smyth Dec 03 '11 at 00:26
  • 3
    This is what I needed, it works with a static method – myro Apr 17 '18 at 08:18
  • 2
    This was the answer I needed because I didn't have control of the classes and base class. – Tim Feb 18 '20 at 21:16
  • 3
    if you replace the hard-coded methode name with this: `var methodeName = nameof(SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection);` You can safely rename the Methode (and get a copiler error/use auto renaming). – RoJaIt Apr 10 '22 at 20:10
  • 2
    @RemiDespres-Smyth I don't see how. What if T implements a different body for a static method? StaticMethodOnSomeBaseClassThatReturnsCollection could be an abstract class with a static method with default return. T could override it. Then the solutions are clearly VERY different. – Shiv Apr 22 '22 at 03:53
  • Definitely would be a lot nicer if this was available via some non-reflection way. Reflection seems so hacky for refactoring risk etc. I'm allergic to stuff like this not being compile time safe! – Shiv Apr 22 '22 at 03:57
12

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();
}
micahneitz
  • 133
  • 2
  • 9
  • 1
    This answer is excellent and solved a rather tricky problem I had! – Andreas Pardeike Aug 31 '21 at 09:11
  • 1
    This answer is fantastic. It basically creates an implicit object cache of `T`s to be used to get the actual method. – Ryan Nov 18 '21 at 06:19
  • This answer cleverly "abuses" the somehow inconsistent rule, that you can, in your base class, create new objects of yet-unknown generic type, inheriting that base class - but you can't access the already-know static fields of that type (T.SomeFileld). Great! Though is seems incompatible with static constructors in derived types (I get "null reference exception" on "private static readonly T value = new();"). – lisz Aug 17 '22 at 11:06
8

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
5

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.

Brad Wilson
  • 67,914
  • 9
  • 74
  • 83
  • 1
    I'm not - I'm working above a generated DAL layer. The generated classes all inherit from a base class, which has a static FetchAll method. I'm trying to reduce code duplication in my repository class with a "generic" repository class - a lot of repeating code, except for the concrete class used. – Remi Despres-Smyth Oct 13 '08 at 04:06
  • 1
    Then why don't you just call SomeBaseClass.StaticMethod...() ? – Brad Wilson Oct 13 '08 at 04:43
  • Sorry, I didn't explain myself well in the previous comment. FetchAll is defined on the base, but implemented on the derived classes. I need to call it on the derived class. – Remi Despres-Smyth Oct 13 '08 at 05:31
  • 7
    If it is a static method, then it is both defined and implemented by the base. There is no such thing as virtual/abstract static method in C#, and no override for such. I suspect you have simply re-declared it, which is very different. – Marc Gravell Oct 13 '08 at 06:55
  • 1
    Yes, you're right - I had made invalid assumptions here. Thanks for the discussion, it's helped put me on the right track. – Remi Despres-Smyth Oct 13 '08 at 16:27
4

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" });
johnc
  • 39,385
  • 37
  • 101
  • 139
4

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.

Amir Abiri
  • 8,847
  • 11
  • 41
  • 57
  • Just to update this answer a bit, because I think this seems like the best pattern. With C# 8 you should be able to enforce static methods via an interface. – Tony Topper Feb 09 '21 at 23:28
  • @TonyTopper I was really hoping that with C# 8s static methods, we'd be able to call `T.SomeStaticMethod()` using constraints like `where T : ISomeInterfaceWithStaticMethods`, but alas it still gives "'T' is a type parameter, which is not valid in the given context". I'm pretty sure the compiler has enough information do resolve the concrete type at compile time, so it's strange that it doesn't work. – Ryan Nov 18 '21 at 06:04
2

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).

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 1
    The problem is that because generics are provided by the runtime, this would probably mean a new CLR version - which they've (largely) avoided since 2.0. Maybe we're due a new one, though... – Marc Gravell Oct 13 '08 at 06:57
1

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();
}
Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
  • 2
    This gives a syntax error for me? What does `public T : SomeBaseClass` mean? – Eric Sep 27 '15 at 21:28
  • If your class has an instance method someInstanceMethod() you can allways call that by doing (new T()).someInstanceMethod(); - but this is calling an instance method - the question asked how to call a static method of the class. – timothy Jun 19 '19 at 10:48