2010-10-12 31 views
31

Quizás estoy sobrecargado, pero esto no se está compilando (CS0411). ¿Por qué?Los argumentos de tipo para el método no se pueden inferir del uso

interface ISignatur<T> 
{ 
    Type Type { get; } 
} 

interface IAccess<S, T> where S : ISignatur<T> 
{ 
    S Signature { get; }  
    T Value { get; set; } 
} 

class Signatur : ISignatur<bool> 
{ 
    public Type Type 
    { 
     get { return typeof(bool); } 
    } 
} 

class ServiceGate 
{ 
    public IAccess<S, T> Get<S, T>(S sig) where S : ISignatur<T> 
    { 
     throw new NotImplementedException(); 
    } 
} 

static class Test 
{ 
    static void Main() 
    { 
     ServiceGate service = new ServiceGate(); 
     var access = service.Get(new Signatur()); // CS4011 error 
    } 
} 

Alguien una idea ¿por qué no? ¿O cómo resolver?

+1

tal vez añadiendo el error que se obtiene ... – vulkanino

+1

respuesta de Eric Lippert aquí, junto con su publicación de blog con la que se vincula, es una buena explicación de por qué. Básicamente, si recuerdo correctamente, el compilador no deducirá tipos utilizando restricciones genéricas.http://stackoverflow.com/questions/3630153/generic-extension-method-type-argument-cannot-be-inferred-from-the-usage/3630202#3630202 –

Respuesta

42

Get<S, T> toma dos argumentos de tipo. Cuando llama al service.Get(new Signatur());, ¿cómo sabe el compilador qué es T? Tendrá que pasarlo explícitamente o cambiar algo más sobre sus jerarquías de tipos. Pasando explícitamente se vería así:

service.Get<Signatur, bool>(new Signatur()); 
+0

Sí, quería evitar la llamada explícita. Pero si a partir de la explicación de Eric Lippert, las restricciones genéricas no se utilizan para resolver los tipos de devolución genéricos, entonces esto no funcionaría. ¡Gracias! – Ben

8

Kirk's answer es correcto. Como regla, no tendrá suerte con la inferencia de tipo cuando la firma de su método tenga menos tipos de parámetros que parámetros de tipo genéricos.

En su caso particular, parece que podría posiblemente mover el parámetro T tipo a nivel de clase y luego obtener la inferencia de tipos en su Get método:

class ServiceGate<T> 
{ 
    public IAccess<S, T> Get<S>(S sig) where S : ISignatur<T> 
    { 
     throw new NotImplementedException(); 
    } 
} 

A continuación, el código que envió con el CS0411 error podría ser reescrita como:

static void Main() 
{ 
    // Notice: a bit more cumbersome to write here... 
    ServiceGate<SomeType> service = new ServiceGate<SomeType>(); 

    // ...but at least you get type inference here. 
    IAccess<Signatur, SomeType> access = service.Get(new Signatur()); 
} 
0

como ya he mencionado en mi comentario, creo que la razón por la que esto no funciona es porque el compilador no puede inferir tipos a base de o n restricciones genéricas.

A continuación se muestra una implementación alternativa que compilará. He revisado la interfaz de IAccess para que solo tenga el parámetro de tipo genérico T.

interface ISignatur<T> 
{ 
    Type Type { get; } 
} 

interface IAccess<T> 
{ 
    ISignatur<T> Signature { get; } 
    T Value { get; set; } 
} 

class Signatur : ISignatur<bool> 
{ 
    public Type Type 
    { 
     get { return typeof(bool); } 
    } 
} 

class ServiceGate 
{ 
    public IAccess<T> Get<T>(ISignatur<T> sig) 
    { 
     throw new NotImplementedException(); 
    } 
} 

static class Test 
{ 
    static void Main() 
    { 
     ServiceGate service = new ServiceGate(); 
     var access = service.Get(new Signatur()); 
    } 
} 
2

Ahora mi objetivo era tener un par con un tipo de base y una definición de tipo (Requisito A). Para la definición de tipo, quiero usar herencia (Requisito B). El uso debe ser posible, sin conocimiento explícito sobre el tipo base (Requisito C).

Después de que sé ahora que las limitaciones gernic no se utilizan para resolver el tipo de retorno genérico, que experimentó un poco:

introducte

nos dejó Ok Get2:

class ServiceGate 
{ 
    public IAccess<C, T> Get1<C, T>(C control) where C : ISignatur<T> 
    { 
     throw new NotImplementedException(); 
    } 

    public IAccess<ISignatur<T>, T> Get2<T>(ISignatur<T> control) 
    { 
     throw new NotImplementedException(); 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     ServiceGate service = new ServiceGate(); 
     //var bla1 = service.Get1(new Signatur()); // CS0411 
     var bla = service.Get2(new Signatur()); // Works 
    } 
} 

bien, pero esto alcances de soluciones no requriement B.

siguiente intento:

class ServiceGate 
{ 
    public IAccess<C, T> Get3<C, T>(C control, ISignatur<T> iControl) where C : ISignatur<T> 
    { 
     throw new NotImplementedException(); 
    } 

} 

class Test 
{ 
    static void Main() 
    { 
     ServiceGate service = new ServiceGate(); 
     //var bla1 = service.Get1(new Signatur()); // CS0411 
     var bla = service.Get2(new Signatur()); // Works 
     var c = new Signatur(); 
     var bla3 = service.Get3(c, c); // Works!! 
    } 
} 

¡Agradable! Ahora el compilador puede inferir los tipos de devolución genéricos. Pero no me gusta Otro intento:

class IC<A, B> 
{ 
    public IC(A a, B b) 
    { 
     Value1 = a; 
     Value2 = b; 
    } 

    public A Value1 { get; set; } 

    public B Value2 { get; set; } 
} 

class Signatur : ISignatur<bool> 
{ 
    public string Test { get; set; } 

    public IC<Signatur, ISignatur<bool>> Get() 
    { 
     return new IC<Signatur, ISignatur<bool>>(this, this); 
    } 
} 

class ServiceGate 
{ 
    public IAccess<C, T> Get4<C, T>(IC<C, ISignatur<T>> control) where C : ISignatur<T> 
    { 
     throw new NotImplementedException(); 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     ServiceGate service = new ServiceGate(); 
     //var bla1 = service.Get1(new Signatur()); // CS0411 
     var bla = service.Get2(new Signatur()); // Works 
     var c = new Signatur(); 
     var bla3 = service.Get3(c, c); // Works!! 
     var bla4 = service.Get4((new Signatur()).Get()); // Better... 
    } 
} 

Mi solución final es tener algo así como ISignature<B, C>, donde B no está en el tipo de base y la definición C ...

Cuestiones relacionadas