无法从用法中推断出方法的类型参数

时间:2010-10-12 17:11:12

标签: c# type-inference

也许我工作过度,但这不是编译(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
    }
}

任何人都知道为什么不呢?或者如何解决?

7 个答案:

答案 0 :(得分:64)

Get<S, T>有两个类型参数。当你致电service.Get(new Signatur());时,编译器如何知道T是什么?您必须明确地传递它或更改有关您的类型层次结构的其他内容。明确地传递它看起来像:

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

答案 1 :(得分:11)

Kirk's answer是正确的。通常情况下,当方法签名的类型的参数少于泛型类型参数时,您不会对类型推断有任何好运。

在您的特定情况下,您似乎可能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());
}

答案 2 :(得分:3)

现在我的目标是让一对具有基本类型和类型定义(要求A)。对于类型定义,我想使用继承(要求B)。应该可以使用,而不需要对基本类型有明确的了解(要求C)。

在我现在知道gernic约束不用于解决泛型返回类型之后,我进行了一些实验:

好的,让我们介绍一下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 :(得分:0)

正如我在评论中提到的,我认为这不起作用的原因是因为编译器无法根据通用约束来推断类型。

以下是将要编译的替代实现。我已将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());
    }
}

答案 4 :(得分:0)

我想举一个简单易懂的例子

如果您调用这样的方法,您的客户将不知道返回类型

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

编译器将不会因为知道返回类型而对您发怒

答案 5 :(得分:0)

我收到此错误是因为我在方法定义中犯了一个错误。我已经声明该方法接受通用类型(在方法名称后注意“ T”):

protected int InsertRecord<T>(CoasterModel model, IDbConnection con)

但是,当我调用该方法时,我没有使用正确的用法:

int count = InsertRecord(databaseToMigrateFrom, con);

我刚刚删除了通用类型转换,它就起作用了。

答案 6 :(得分:0)

对于那些想知道为什么这适用于 Java 而不适用于 C# 的人,请考虑如果某个 doof 编写此类会发生什么:

public class Trololol : ISignatur<bool>, ISignatur<int>{
    Type ISignatur<bool>.Type => typeof(bool);
    Type ISignatur<int>.Type => typeof(int);
}

编译器应该如何解析 var access = service.Get(new Trololol())intbool 都有效。

这种隐式解析在 Java 中起作用的原因可能与擦除有关,以及如果您尝试实现具有两个或多个不同类型参数的接口,Java 将如何处理。这样的类在 Java 中是不允许的,但在 C# 中就可以了。