【问题标题】:calling an overloaded method in generic method在泛型方法中调用重载方法
【发布时间】:2014-03-15 17:40:16
【问题描述】:
class Test
{
    public BinaryWriter Content { get; private set; }
    public Test Write<T> (T data)
    {
        Content.Write(data);
        return this;
    }
}

它不会编译。

1. The best overloaded method match for 'System.IO.BinaryWriter.Write(bool)' has some invalid arguments
2. Argument 1: cannot convert from 'T' to 'bool'

似乎 Test.Write 总是试图调用 BinaryWriter.Write(bool)。有什么建议吗?

【问题讨论】:

标签: c#


【解决方案1】:

重载解析发生在编译时,在这种情况下,对 T 一无所知,因此不适用重载。

   class Test
    {
        public BinaryWriter Content { get; private set; }
        public Test Write<T>(T data)
        {
            Content.Write((dynamic)data);
            return this;
        }
    }

当然,这可能会造成一些问题。例如,如果您将 DateTime 发送到方法,应用程序将编译得很好。但是,它会抛出异常。

【讨论】:

  • 也许可以解决问题,但这种方式很危险,容易出现异常。
  • 其实这种情况下不需要是泛型方法。
【解决方案2】:

这是可能的,但不适用于通用约束。您可以尝试使用反射,并根据T的类型获取适当的Write方法重载版本。

在您的构造函数中使用Stream 初始化您的BinaryWriter,然后使用如下反射:

class Test
{
    public BinaryWriter Content { get; private set; }

    public Test(Stream stream)
    {
        Content = new BinaryWriter(stream);
    }

    public Test Write<T>(T data)
    {

        var method = typeof (BinaryWriter).GetMethod("Write", new[] {data.GetType()});
        if (method != null)
        {
            method.Invoke(Content, new object[] { data });
        }
        // here you might want to throw an exception if method is not found

        return this;
    }
}

这是一个测试程序:

Test writer;
using (var fs = new FileStream("sample.txt", FileMode.Open))
{
     writer = new Test(fs);
     writer = writer.Write(232323);
     writer = writer.Write(true);
     writer = writer.Write(12);
}

using (var fs = File.Open("sample.txt", FileMode.Open))
{
    var reader = new BinaryReader(fs);
    Console.WriteLine(reader.ReadInt32());  // 232323
    Console.WriteLine(reader.ReadBoolean()); // true
    Console.WriteLine(reader.ReadByte());    // 12
}

【讨论】:

    【解决方案3】:

    在您的 write 方法中,T 是泛型的,这意味着 T 可以是任何东西,但 BinaryWriter.Write 不会采用泛型重载。所以你不能这样做。

    【讨论】:

      【解决方案4】:

      你不能那样做。

      由于T 的真实类型在编译时未知,编译器找不到合适的Write 重载,并尝试调用第一个找到的。没有重载,可以用任何类型的T(例如Write(object))调用。

      【讨论】:

        【解决方案5】:
        1. 使用反射会大大减慢方法调用的速度。它还将参数类型检查从编译时移到运行时,这在大多数情况下是不可取的。

        2. 在这种情况下使用动态调用只是之前方法的一个花哨且略微优化的版本,具有相同的缺点。

        3. 正确的做法是复制所有的重载:

          public Test Write (bool data)
          {
              Content.Write(data);
              return this;
          }
          
          public Test Write (byte data)
          {
              Content.Write(data);
              return this;
          }
          
          public Test Write (byte[] data)
          {
              Content.Write(data);
              return this;
          }
          
          public Test Write (char data)
          {
              Content.Write(data);
              return this;
          }
          
          public Test Write (char[] data)
          {
              Content.Write(data);
              return this;
          }
          
          // ...
          

          此方法非常冗长,但它是唯一提供参数类型的编译时检查的方法,并且在选择重载和编译时时性能最高。此外,这些方法很可能是内联的。

          在这种情况下,我通常使用 T4 脚本,它基于反射生成上述代码。创建一个TT文件,枚举Write重载BinaryWriter,生成代码。

        【讨论】:

        • 最烦人的是我永远看不到-1的解释。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-18
        • 2013-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多