2010-10-12 22 views
31

Może jestem przepracowany, ale to nie jest kompilacja (CS0411). Czemu?Argumentów typu dla metody nie można wywnioskować z użycia

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 
    } 
} 

Każdy pomysł, dlaczego nie? Lub jak rozwiązać?

+1

może dodawania błędu można dostać ... – vulkanino

+1

odpowiedzi Eric Lippert za tutaj, wraz ze swoim blogiem, do którego prowadzi link, jest całkiem dobrym wyjaśnieniem, dlaczego. Zasadniczo, jeśli dobrze pamiętam, kompilator nie wywnioskuje typów przy użyciu ogólnych ograniczeń.http://stackoverflow.com/questions/3630153/generic-extension-method-type-argument-cannot-be-inferred-from-the-usage/3630202#3630202 –

Odpowiedz

42

Get<S, T> przyjmuje dwa argumenty typu. Kiedy dzwonisz pod numer service.Get(new Signatur());, skąd kompilator wie, co to jest T? Musisz go przekazać jawnie lub zmienić coś innego na temat hierarchii typów. Mijając go wyraźnie wyglądałby następująco:

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

Tak, chciałem uniknąć wywołania explicite. Ale jeśli, jak wyjaśnia Eric Lippert, ogólne ograniczenia nie są używane do rozwiązywania ogólnych typów zwrotu, to nie zadziałałoby. Dziękuję Ci! – Ben

8

Kirk's answer ma rację w sprawie. Z reguły nie będziesz mieć szczęścia przy wnioskach typu, gdy twój podpis metody ma mniej typów parametrów niż ma ogólnych parametrów typu.

W danym przypadku, wydaje się, mogłyby ewentualnie przenieść parametr T typu na poziomie klasy, a następnie uzyskać typ wnioskowania o swojej metodzie Get:

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

wówczas kod, który pisał z CS0411 błąd może być zapisane jako:

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

jak wspomniałem w moim komentarzu, myślę, że powodem, dla którego to nie działa tak, ponieważ kompilator nie może wnioskować o typy oparte n ogólne ograniczenia.

Poniżej znajduje się alternatywna implementacja, która zostanie skompilowana. Zrewidowałem interfejs IAccess, aby miał tylko ogólny parametr typu 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

Teraz moim celem była jedna para z typem podstawowym i definicją typu (wymaganie A). Dla definicji typu chcę używać dziedziczenia (Wymaganie B). Użycie powinno być możliwe bez wyraźnej wiedzy o typie bazowym (wymaganie C).

Po Teraz wiem, że gernic ograniczenia nie są wykorzystywane do rozwiązywania ogólny typ zwracany, eksperymentowałem trochę:

introducte

OK Chodźmy 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 
    } 
} 

w porządku, ale to rozwiązanie biegu nie requriement B.

Następny try:

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!! 
    } 
} 

Nice! Teraz kompilator może wywnioskować ogólne typy zwracane. Ale nie podoba mi się to. Inne try:

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... 
    } 
} 

Moje ostateczne rozwiązanie jest mieć coś ISignature<B, C>, gdzie B ist typu bazowego i C definicji ...

Powiązane problemy