web-dev-qa-db-ja.com

メソッドの型引数は使用法から推測できません

たぶん私は過労ですが、これはコンパイルしていません(CS0411)。どうして?

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

誰でもアイデアはなぜですか?または解決方法は?

49
Ben

_Get<S, T>_は2つの型引数を取ります。 service.Get(new Signatur());を呼び出すと、コンパイラはTが何であるかをどのように認識しますか?明示的に渡すか、型階層に関する何かを変更する必要があります。明示的に渡すと次のようになります。

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

カークの答え は正解です。原則として、メソッドシグネチャのgeneric type parametersの数よりも少ないtypes of parametersの場合、型推論の運はありません。

あなたの特定のケースでは、[可能性T型パラメーターをクラスレベルに移動し、Getメソッドで型推論を取得できるようです。

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

次に、CS0411エラーで投稿したコードを次のように書き換えることができます。

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

今、私の目的は、基本型と型定義(要件A)を持つ1つのペアを持つことでした。型定義には、継承を使用します(要件B)。基本型についての明示的な知識がなくても、使用できるはずです(要件C)。

ジェネリックな戻り値の型を解決するためにゲルの制約が使用されていないことがわかった後、少し実験しました。

では、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
    }
}

結構ですが、この解決策は要求Bには達しません。

次の試行:

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

いいね!これで、コンパイラは一般的な戻り値の型を推測できます。しかし、私はそれが好きではありません。その他の試行:

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

私の最終的な解決策は、ISignature<B, C>、ここでBは基本型、Cは定義です...

3
Ben

私のコメントで述べたように、これが機能しない理由は、コンパイラが一般的な制約に基づいて型を推測できないためだと思います。

以下は、コンパイルされる代替実装です。 IAccessインターフェイスを修正して、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());
    }
}

シンプルでわかりやすい例を作りたかった

このようなメソッドを呼び出すと、クライアントは戻り値の型を知りません

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

次に、コンパイラに戻り値の型がList<InterestPointTypeMap>

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

コンパイラは、戻り値の型を知っているので、あなたに怒っません。

0
Hamit YILDIRIM