Estoy teniendo mucha diversión Funcy (diversión intencionada) con métodos genéricos. En la mayoría de los casos, la inferencia tipo C# es lo suficientemente inteligente como para descubrir qué argumentos genéricos debe usar en mis métodos genéricos, pero ahora tengo un diseño donde el compilador C# no tiene éxito, aunque creo que podría haber tenido éxito en encontrar el tipos correctos.¿Por qué C# no infiere mis tipos genéricos?
¿Alguien puede decirme si el compilador es un poco tonto en este caso, o hay una razón muy clara por la cual no puede inferir mis argumentos genéricos?
Aquí está el código:
Clases y definiciones de interfaz:
interface IQuery<TResult> { }
interface IQueryProcessor
{
TResult Process<TQuery, TResult>(TQuery query)
where TQuery : IQuery<TResult>;
}
class SomeQuery : IQuery<string>
{
}
un código que no se compila:
class Test
{
void Test(IQueryProcessor p)
{
var query = new SomeQuery();
// Does not compile :-(
p.Process(query);
// Must explicitly write all arguments
p.Process<SomeQuery, string>(query);
}
}
¿Por qué es esto? ¿Que me estoy perdiendo aqui?
Aquí está el mensaje de error del compilador (que no deja mucho a la imaginación):
Los argumentos de tipo de método IQueryProcessor.Process (TQuery) no puede deducirse del uso. Intente especificar explícitamente los argumentos de tipo .
La razón creo que C# debe ser capaz de inferir que es por lo siguiente:
- Puedo proporcionar un objeto que implementa
IQuery<TResult>
. - Que solo la versión
IQuery<TResult>
que implementa el tipo esIQuery<string>
y por lo tanto TResult debe serstring
. - Con esta información el compilador tiene TResult y TQuery.
SOLUCIÓN
Para mí la mejor solución era cambiar la interfaz IQueryProcessor
y el uso de tipado dinámico en la implementación:
public interface IQueryProcessor
{
TResult Process<TResult>(IQuery<TResult> query);
}
// Implementation
sealed class QueryProcessor : IQueryProcessor {
private readonly Container container;
public QueryProcessor(Container container) {
this.container = container;
}
public TResult Process<TResult>(IQuery<TResult> query) {
var handlerType =
typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
dynamic handler = container.GetInstance(handlerType);
return handler.Handle((dynamic)query);
}
}
La interfaz IQueryProcessor
ahora toma en un parámetro IQuery<TResult>
. De esta manera puede devolver un TResult
y esto resolverá los problemas desde la perspectiva del consumidor. Necesitamos usar la reflexión en la implementación para obtener la implementación real, ya que los tipos de consulta concretos son necesarios (en mi caso). Pero aquí viene el tipeo dinámico al rescate que nos hará reflexionar. Puede leer más sobre esto en este article.
¿Cuál es el mensaje de error? –
El compilador no sabe qué 'TResult' usar. En el momento en que necesita tomar la decisión, no sabe que la va a poner en una 'cadena' (así que tal vez debería inferir 'cadena').E incluso si lo supiera, también podría ser legalmente 'Process' donde 'customClass' es cualquier clase derivada de' string'. Ver también: [La inferencia del tipo de devolución no funciona en los grupos miembros] (http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference- does-not-work-on-member-groups.aspx). –
Y @Raymond dice que no es un tipo de .NET ... pshaw –