2008-10-13 13 views
82

Tenía la esperanza de hacer algo como esto, pero parece que es ilegal en C#:Llamar a un método estático en un parámetro de tipo genérico


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

me sale un error en tiempo de compilación: " 'T' es un 'parámetro de tipo', que no es válido en el contexto dado. "

Dado un parámetro de tipo genérico, ¿cómo puedo llamar a un método estático en la clase genérica? El método estático debe estar disponible, dada la restricción.

+4

Ver http://blogs.msdn.com/b/ericlippert/archive/2007/06/ 14/calling-static-methods-on-type-parameters-is-illegal-part-one.aspx, y http://blogs.msdn.com/b/ericlippert/archive/2007/06/18/calling-static -methods-on-type-parameters-is-illegal-part-two.aspx y http://blogs.msdn.com/b/ericlippert/archive/2007/06/21/3445650.aspx para obtener más información sobre este tema. –

Respuesta

47

En este caso, debe llamar directamente al método estático en el tipo restringido. C# (y CLR) no son compatibles con los métodos estáticos virtuales. Por lo tanto:

T.StaticMethodOnSomeBaseClassThatReturnsCollection 

... puede haber diferente que:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection 

El paso por el parámetro de tipo genérico es una indirección que no sean necesarios y por lo tanto no es compatible.

+18

Pero, ¿y si enmascararas tu método estático en una clase para niños? SomeChildClass clase pública: SomeBaseClass { nueva StaticMethodOnSomeBaseClassThatReturnsCollection pública estática() {}} Podría hacer algo para tener acceso a ese método estático de un tipo genérico? –

+2

Eche un vistazo a la respuesta de Joshua Pech a continuación, creo que funcionaría en ese caso. –

+0

¿Devolvería SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection(); 'work? Si es así, es posible que desee agregar eso a su respuesta. Gracias. Funcionó para mí En mi caso, mi clase tenía un param tipo, así que 'devolvió SomeBaseClass .StaticMethodOnSomeBaseClassThatReturnsCollection();' y funcionó. – toddmo

2

A partir de ahora, no se puede. Necesita una forma de decirle al compilador que T tiene ese método, y en este momento, no hay forma de hacerlo. (Muchos están presionando a Microsoft para expandir lo que se puede especificar en una restricción genérica, por lo que tal vez esto sea posible en el futuro).

+1

El problema es que, dado que los genéricos son proporcionados por el tiempo de ejecución, esto probablemente significaría una nueva versión de CLR, que han evitado (en gran medida) desde 2.0. Tal vez nos merezcamos uno nuevo, aunque ... –

4

Parece que está intentando usar genéricos para evitar el hecho de que no hay "métodos estáticos virtuales" en C#.

Desafortunadamente, eso no va a funcionar.

+1

No estoy - Estoy trabajando por encima de una capa DAL generada. Todas las clases generadas heredan de una clase base, que tiene un método FetchAll estático. Estoy tratando de reducir la duplicación de código en mi clase de repositorio con una clase de repositorio "genérico", una gran cantidad de código repetitivo, a excepción de la clase concreta utilizada. –

+1

Entonces, ¿por qué no llamas a SomeBaseClass.StaticMethod ...()? –

+0

Lo siento, no me expliqué bien en el comentario anterior. FetchAll se define en la base, pero se implementa en las clases derivadas. Necesito llamarlo en la clase derivada. –

7

La única forma de llamar a un método de este tipo sería a través de la reflexión. Sin embargo, parece que sería posible ajustar esa funcionalidad en una interfaz y usar un patrón IoC/factory/etc basado en instancia.

2

Aquí, puedo enviar un ejemplo que funciona, es una solución

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

Esto me da un error de sintaxis? ¿Qué significa 'public T: SomeBaseClass'? – Eric

1

Usted debe ser capaz de hacer esto utilizando la reflexión, tal como se describe here

18

Para elaborar en una respuesta anterior, creo la reflexión está más cerca de lo que quieres aquí. Podría dar 1001 razones por las que debería o no hacer algo, solo responderé a su pregunta como se lo solicite. Creo que deberías llamar al método GetMethod sobre el tipo del parámetro genérico e ir desde allí. Por ejemplo, para una función:

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

Donde T es cualquier clase que tenga el método estático fetchAll().

Sí, soy consciente de que esto es terriblemente lento y puede fallar si algún padre no fuerza a todas sus clases secundarias a implementar fetchAll pero responde la pregunta como se le pidió.

+2

No, en absoluto. JaredPar lo tiene todo a la perfección: T.StaticMethodOnSomeBaseClassThatReturnsCollection donde T: SomeBaseClass no es diferente a simplemente declarar SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection. –

1

Solo quería tirarlo por ahí que a veces los delegados resuelven estos problemas, dependiendo del contexto.

Si necesita llamar al método estático como una especie de fábrica o método de inicialización, puede declarar un delegado y pasar el método estático a la fábrica genérica pertinente o lo que sea que necesite esta "clase genérica con este método estático ".

Por ejemplo:

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(); 
    } 
} 

Lamentablemente no se puede hacer cumplir la clase que tiene el método correcto, pero puede al menos en tiempo de compilación reforzar que el método de fábrica resultante tiene todo lo que espera (es decir, una método de inicialización con exactamente la firma correcta). Esto es mejor que una excepción de reflexión de tiempo de ejecución.

Este enfoque también tiene algunos beneficios, es decir, puede volver a utilizar los métodos de arranque, haga que sean los métodos de instancia, etc.

Cuestiones relacionadas