c# - with - Una lista<> de Func<> s, error de compilación con el tipo de retorno genérico, pero ¿por qué?
how do you declare a generic method called test which takes one parameter which must be a value type (4)
C # solo permite la covarianza en las interfaces. Eso significa que no puede emitir un RetVal<MyDerived1>
a un RetVal<MyBase>
automáticamente. Si RetVal
debe ser covariante, cree una interfaz para él, de esta manera:
public interface IRetVal<out T>
{
}
public class RetVal<T> : IRetVal<T> where T : MyBase { /* body omitted */ }
public class Test
{
public IRetVal<T> GetRetValT<T>() where T : MyBase
{
return null;
}
}
Entonces este código funcionará:
var list2 = new List<Func<IRetVal<MyBase>>>
{
test.GetRetValT<MyDerived1>,
test.GetRetValT<MyDerived2>
};
Esta es una pregunta un poco larga, así que por favor tengan paciencia conmigo.
Necesito crear una asignación entre un conjunto de cadenas y las correspondientes llamadas de método genéricas para cada cadena. Sin embargo, me he encontrado con un problema de compilación, se explica más abajo.
En mi caso, estoy usando un Dictionary<>
, pero el problema existe igualmente para una List<>
. Para simplificar, estoy usando una List<>
en el siguiente ejemplo.
Considere estas tres clases:
public abstract class MyBase { /* body omitted */ }
public class MyDerived1 : MyBase { /* body omitted */ }
public class MyDerived2 : MyBase { /* body omitted */ }
Y un método en alguna otra clase:
public class Test
{
public T GetT<T>() where T : MyBase { /* body omitted */ }
}
En otra clase, puedo declarar una List<Func<MyBase>>
así:
public class SomeClass
{
public void SomeFunc()
{
var test = new Test();
var list1 = new List<Func<MyBase>>
{
test.GetT<MyDerived1>,
test.GetT<MyDerived2>
};
}
}
Todo esto está bien y bien.
Pero, qué pasa si quiero tener una función que devuelva una clase genérica como esta:
public class RetVal<T> where T : MyBase { /* body omitted */ }
public class Test
{
public RetVal<T> GetRetValT<T>() where T : MyBase
{
return null;
}
}
Y quiero crear una List<>
equivalente List<>
usando esta función. es decir, una lista >>?
public class Class1
{
public void SomeFunc()
{
var test = new Test();
var list2 = new List<Func<RetVal<MyBase>>>
{
test.GetRetValT<MyDerived1>, // compile error
test.GetRetValT<MyDerived2> // compile error
};
}
}
Recibo errores de compilación de Expected a method with ''RetVal<MyBase> GetRetValT()'' signature
.
Entonces, ¿hay alguna forma de evitar esto, o hay un enfoque alternativo que pueda usar para crear mi cadena ... asignaciones genéricas de llamadas a métodos ?
El problema es la clásica covarianza / contravarianza de los genéricos. Está asumiendo que debido a que MyDerived1
y MyDerived2
heredan de MyBase
, que un RetVal<MyDerived1>
hereda de RetVal<MyBase>
, y no lo hace.
La forma más fácil de solucionar este problema es probablemente cambiar el código a:
var list2 = new List<Func<RetVal<MyBase>>>
{
() => (MyBase)test.GetRetValT<MyDerived1>,
() => (MyBase)test.GetRetValT<MyDerived2>
};
o mejor aún, como JS señala en los comentarios, simplemente cambie RetVal<T>
para que sea covariante si es posible:
public interface IRetVal<out T> { ... }
public class RetVal<T> : IRetVal<T> { ... }
Los parámetros de tipo genérico para las clases no pueden ser covariantes, pero pueden ser covariantes para las interfaces. Puede hacer lo que quiera con una interfaz IRetVal<T>
lugar de la clase RetVal<T>
, si el parámetro de tipo se declara como covariante. Para declarar un parámetro de tipo de interfaz como covariante, debe usarse solo en las posiciones de "salida".
Para ilustrar, este código no compilará:
interface IRetVal<out T>
{
T Value { get; }
void AcceptValue(T value);
}
Para obtener el código para compilar, debe eliminar el modificador de out
del parámetro de tipo, o eliminar el método AcceptValue (porque usa T para un parámetro: una posición de entrada).
Con la IRetVal<out T>
, puedes hacer esto:
public class MyBase { }
public class MyDerived1 : MyBase { }
public class MyDerived2 : MyBase { }
public interface IRetVal<out T> where T : MyBase { /* body omitted */ }
public class Test
{
public IRetVal<T> GetRetValT<T>() where T : MyBase
{
return null;
}
}
public class Class1
{
public void SomeFunc()
{
var test = new Test();
var list = new List<Func<IRetVal<MyBase>>>
{
test.GetRetValT<MyDerived1>,
test.GetRetValT<MyDerived2>
};
}
}
Tuve un problema similar recientemente.
C # no admite la covarianza del tipo de retorno para los fines de la implementación de la interfaz o la invalidación del método virtual. Vea esta pregunta para más detalles:
¿C # admite la covarianza de tipo de retorno? .
Usted puede ser capaz de hackear esto, haciendo mi:
public RetVal<R> GetRetValT<T,R>() where T : MyBase where R : MyBase
{
return null;
}
//Then, change this to:
test.GetRetValT<MyDerived1, MyBase>