web-dev-qa-db-fra.com

Les arguments de type pour la méthode ne peuvent pas être déduits de l'utilisation

Je suis peut-être surmené, mais ce n'est pas en train de compiler (CS0411). Pourquoi?

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

Quelqu'un a une idée pourquoi pas? Ou comment résoudre?

49
Ben

Get<S, T> Prend deux arguments de type. Lorsque vous appelez service.Get(new Signatur()); comment le compilateur sait-il ce qu'est T? Vous devrez le passer explicitement ou changer quelque chose d'autre sur vos hiérarchies de types. Le passer explicitement ressemblerait à:

service.Get<Signatur, bool>(new Signatur());
64
Kirk Woll

la réponse de Kirk est juste. En règle générale, vous n'aurez pas de chance avec l'inférence de type lorsque la signature de votre méthode a moins types de paramètres qu'elle n'en a paramètres de type générique .

Dans votre cas particulier, il semble que vous pourriez éventuellement déplacer le paramètre de type T au niveau de la classe, puis obtenir une inférence de type sur votre Get méthode:

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

Ensuite, le code que vous avez publié avec l'erreur CS0411 peut être réécrit comme suit:

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

Mon objectif était maintenant d'avoir une paire avec un type de base et une définition de type (exigence A). Pour la définition de type, je veux utiliser l'héritage (exigence B). L'utilisation doit être possible, sans connaissance explicite du type de base (exigence C).

Après avoir su maintenant que les contraintes gerniques ne sont pas utilisées pour résoudre le type de retour générique, j'ai expérimenté un peu:

Ok, introduisons 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
    }
}

Très bien, mais cette solution n'atteint pas le critère B.

Essayez ensuite:

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

Agréable! Le compilateur peut désormais déduire les types de retour génériques. Mais je n'aime pas ça. Autre essai:

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

Ma dernière solution est d'avoir quelque chose comme ISignature<B, C>, où B est le type de base et C la définition ...

3
Ben

Comme je l'ai mentionné dans mon commentaire, je pense que la raison pour laquelle cela ne fonctionne pas est que le compilateur ne peut pas déduire des types basés sur des contraintes génériques.

Voici une implémentation alternative qui se compilera. J'ai révisé l'interface IAccess pour n'avoir que le paramètre de type générique 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());
    }
}
0

Je voulais faire un exemple simple et compréhensible

si vous appelez une méthode comme celle-ci, votre client ne saura pas le type de retour

var interestPoints = Mediator.Handle(new InterestPointTypeRequest
            {
                LanguageCode = request.LanguageCode,
                AgentId = request.AgentId,
                InterestPointId = request.InterestPointId,
            });

Ensuite, vous devriez dire au compilateur que je sais que le type de retour est List<InterestPointTypeMap>

var interestPoints  = Mediator.Handle<List<InterestPointTypeMap>>(new InterestPointTypeRequest
            {
                LanguageCode = request.LanguageCode,
                AgentId = request.AgentId,
                InterestPointId = request.InterestPointId,
                InterestPointTypeId = request.InterestPointTypeId
            });

le compilateur ne sera plus en colère contre vous pour connaître le type de retour

0
Hamit YILDIRIM