【问题标题】:The type arguments for method cannot be inferred from the usage无法从用法中推断出方法的类型参数
【发布时间】:2010-10-12 17:11:11
【问题描述】:

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

有人知道为什么不?或者怎么解决?

【问题讨论】:

  • 可能会添加你得到的错误...
  • Eric Lippert 的回答以及他链接到的博客文章很好地解释了原因。基本上,如果我没记错的话,编译器不会使用泛型约束来推断类型。 stackoverflow.com/questions/3630153/…

标签: c# type-inference


【解决方案1】:

Get&lt;S, T&gt; 接受两个类型参数。当您调用service.Get(new Signatur()); 时,编译器如何知道T 是什么?您必须明确传递它或更改有关您的类型层次结构的其他内容。显式传递它看起来像:

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

【讨论】:

  • 是的,我想避免显式调用。但是,如果像 Eric Lippert 解释的那样,通用约束不用于解决通用返回类型,那么这将不起作用。谢谢!
  • 我不知道它是怎么知道/不知道的,但是可以推断出T,因此错误信息具有误导性,表明您犯了一些逻辑错误。 SSignature 并且它实现了 ISignatur&lt;bool&gt; 所以在这种情况下 T 不能是 bool 以外的其他类型,并且这两种类型都是已知的。错误应该说“编译器不能......”而不是“它不能......”
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    现在我的目标是让一对具有基本类型和类型定义(要求 A)。对于类型定义,我想使用继承(要求 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&lt;B, C&gt; 的东西,其中 B 是基本类型,C 是定义...

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

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

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

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

        那你应该告诉编译器我知道返回类型是List&lt;InterestPointTypeMap&gt;

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

        编译器不会再因为知道返回类型而生你的气

        【讨论】:

          【解决方案6】:

          我收到此错误是因为我的方法定义有误。我已经声明该方法接受泛型类型(注意方法名称后面的“T”):

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

          但是,当我调用该方法时,我没有使用在我的情况下是正确用法的类型:

          int count = InsertRecord(databaseToMigrateFrom, con);
          

          我刚刚删除了通用转换并且它起作用了。

          【讨论】:

            【解决方案7】:

            对于那些想知道为什么这适用于 Java 而不是 C# 的人,考虑一下如果某些笨蛋写了这个类会发生什么:

            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# 中就可以了。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-05-09
              • 2023-04-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多