Calling a static method on a generic type parameter
Asked Answered
E

10

128

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.

Electromotor answered 13/10, 2008 at 3:49 Comment(3)
See blogs.msdn.com/b/ericlippert/archive/2007/06/14/…, and blogs.msdn.com/b/ericlippert/archive/2007/06/18/… and blogs.msdn.com/b/ericlippert/archive/2007/06/21/3445650.aspx for more on this topic.Martine
The links in the comment from @EricLippert above are no longer valid. The articles can now be found here, here and here.Steerage
@BillTür Thanks! I will eventually move those over to ericlippert.com but it is a slow process.Martine
G
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.

Geniagenial answered 13/10, 2008 at 8:8 Comment(3)
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?Marrowfat
Check out Joshua Pech's answer below, I believe that would work in that case.Electromotor
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<T>.StaticMethodOnSomeBaseClassThatReturnsCollection(); and that worked.Bronk
P
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.

Poseur answered 2/12, 2011 at 15:31 Comment(6)
No, not at all. JaredPar got it absolutely right: T.StaticMethodOnSomeBaseClassThatReturnsCollection where T : SomeBaseClass is no different than simply stating SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection.Electromotor
This is what I needed, it works with a static methodBernardinabernardine
This was the answer I needed because I didn't have control of the classes and base class.Termitarium
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).Definite
@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.Oxyacetylene
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!Oxyacetylene
N
14

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();
}
Neustria answered 3/4, 2021 at 3:11 Comment(3)
This answer is excellent and solved a rather tricky problem I had!Cabob
This answer is fantastic. It basically creates an implicit object cache of Ts to be used to get the actual method.Wildermuth
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();").Raymonderaymonds
M
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.

Miquelmiquela answered 13/10, 2008 at 4:6 Comment(0)
F
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.

Fattish answered 13/10, 2008 at 3:56 Comment(5)
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.Electromotor
Then why don't you just call SomeBaseClass.StaticMethod...() ?Fattish
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.Electromotor
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.Miquelmiquela
Yes, you're right - I had made invalid assumptions here. Thanks for the discussion, it's helped put me on the right track.Electromotor
F
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" });
Fiesole answered 13/2, 2011 at 4:3 Comment(0)
I
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.

Ileostomy answered 28/12, 2011 at 15:8 Comment(2)
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.Smolt
@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.Wildermuth
H
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).

Holmic answered 13/10, 2008 at 3:53 Comment(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...Miquelmiquela
N
2

Fast forward 15 years and in C# 11 this is now possible! Declare your interface methods using static abstract or static virtual (with a body) and they will be accessible directly off the type, as opposed to an instance.

public interface ICustomIntCollection
{
    static abstract Collection<int> Get();
    
    // or
    
    static virtual Collection<int> Get() => new List<int>();
}


public Collection<int> MethodThatFetchesSomething<T>()
    where T : SomeBaseClass, ICustomCollection
{
    return T.Get();
}
Nominative answered 16/10, 2023 at 21:44 Comment(0)
S
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();
}
Sturdivant answered 13/10, 2008 at 3:49 Comment(2)
This gives a syntax error for me? What does public T : SomeBaseClass mean?Nolen
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.Rosaniline

© 2022 - 2024 — McMap. All rights reserved.