【问题标题】:How to use generics to pass argument to a non-generic method?如何使用泛型将参数传递给非泛型方法?
【发布时间】:2014-06-08 17:46:22
【问题描述】:

为什么下面的代码不能编译? 如果泛型类型是“int”、“bool”、“char”等,我如何创建一个调用适当的“BitConverter.GetBytes”重载的泛型方法? 更一般地,如何创建一个基于泛型参数类型调用非泛型方法的泛型方法?

using System;

public class Test
{
    public static void Main()
    {
      var f = new Foo();
      f.GetBytes(10); // should call BitConverter.GetBytes(int);
      f.GetBytes(true); // should call BitConverter.GetBytes(bool);
      f.GetBytes('A'); // should call BitConverter.GetBytes(char);
    }
}

public class Foo
{
    public byte[] GetBytes <TSource> (TSource input)
    {
      BitConverter.GetBytes(input);
    }
}

【问题讨论】:

  • 下面的答案回答了我的问题;在保持编译时类型安全且不会导致运行时性能损失的情况下,这实际上是不可能的。所以,我只是为我的方法应该接受的输入类型创建自己的重载 GetBytes 方法,然后做一些工作,最后调用适当的 BitConverter.GetBytes。

标签: c# generics


【解决方案1】:

更一般地说,如何创建一个基于泛型参数类型调用非泛型方法的泛型方法?

一般来说,你不能,除非有问题的方法将System.Object 作为参数。问题是泛型不仅仅局限于方法调用参数所允许的类型。

你能做的最接近的是使用运行时绑定:

public byte[] GetBytes <TSource> (TSource input)
{
     dynamic obj = input;
     BitConverter.GetBytes(obj);
}

这会将方法绑定逻辑推送到运行时,如果没有合适的方法可以调用,则会抛出。

【讨论】:

  • 您可以在 TSource 上添加一些约束以提供一些帮助,但如果不检查类型,它当然不会是防弹的,这完全违背了它是通用的目的。
  • @Kevin 在这种情况下,无法添加有效的约束,因为您需要类似“是布尔值还是浮点数或整数”之类的东西,这是不允许的.需要重载来支持类型中的那种“分支”。
  • 我同意...我的意思是像where TSource: struct 这样的东西当然不会完美,但可能会稍微好一点。
  • @Kevin 仍然不会让它编译,除非您调用的方法是具有该约束的通用方法。
  • 我说的纯粹是在您的 GetBytes 实现中添加约束,以限制允许使用的类型,但保留它的 DLR 使用,以便编译。
【解决方案2】:

这不起作用的原因是泛型方法仍然静态地解析对它们内部方法的调用。由于TSource 可以是任何类型,它只能调用BitConverter 上的方法,该方法采用object 参数。由于不存在,因此编译失败。

获得您想要的行为的唯一方法是使用dynamic

public byte[] GetBytes <TSource> (TSource input)
{
    BitConverter.GetBytes((dynamic)input);
}

虽然泛型参数现在是多余的,而且你没有类型安全性。

在这种情况下,您可以创建多个匹配的重载,例如

public byte[] GetBytes(bool b) { ... }
public byte[] GetBytes(int i) { ... }

或采用Func&lt;T, byte[]&gt; 参数并包装您需要的每个BitConverter 方法,例如

public void DoSomething<T>(T input, Func<T, byte[]> f)
{
    byte[] bytes = f(input);
    //handle bytes
}
DoSomething(true, BitConverter.GetBytes);

这可能会给你更多的灵活性。

【讨论】:

    【解决方案3】:

    代码调用BitConverter.GetBytes的地方,类型是TSource,所以调用不能被编译器静态绑定。您可以使用动态调用来解决这个问题,这意味着它会很好地编译,然后在运行时得到解决:

    …
    public byte[] GetBytes(dynamic input)
    {
        return BitConverter.GetBytes(input);
    }
    

    您将因使用动态调用而付出性能损失,如果没有合适的调用方法可用,您将获得运行时异常。

    【讨论】:

      【解决方案4】:

      鉴于BitConverter.GetBytes“仅”有 10 个重载,像这样明确地反映它们并非不可能:

      public class Foo
      {
          public byte[] GetBytes(bool input) { return BitConverter.GetBytes(input); }
          public byte[] GetBytes(char input) { return BitConverter.GetBytes(input); }
          public byte[] GetBytes(double input) { return BitConverter.GetBytes(input); }
          public byte[] GetBytes(float input) { return BitConverter.GetBytes(input); }
          public byte[] GetBytes(int input) { return BitConverter.GetBytes(input); }
          public byte[] GetBytes(short input) { return BitConverter.GetBytes(input); }
          public byte[] GetBytes(long input) { return BitConverter.GetBytes(input); }
          public byte[] GetBytes(uint input) { return BitConverter.GetBytes(input); }
          public byte[] GetBytes(ulong input) { return BitConverter.GetBytes(input); }
          public byte[] GetBytes(ushort input) { return BitConverter.GetBytes(input); }
      }
      

      它不是通用的(您所要求的),并且不能扩展到更复杂的示例,但如果数字很小,那么它是一种考虑的方法。

      【讨论】:

        【解决方案5】:

        如果您愿意降低性能,您可以使用反射和名为 GetBytes 的对象扩展。例子……

        public static class Extensions
        {
            #region Fields
            public static Type bcType;
            #endregion
        
            #region Constructor
            static Extensions()
            {
                bcType = typeof(BitConverter);
            }
            #endregion
            public static byte[] GetBytes(this object value)
            {
                Type typeObj = value.GetType();
                MethodInfo miGetBytes = bcType.GetMethod("GetBytes", new Type[] { typeObj });
                if (miGetBytes == null)
                    throw new InvalidOperationException("Method: GetBytes on BitConverter does not have an overload accepting one paramter of type: " + typeObj.FullName);
                byte[] bytesRet = (byte[])miGetBytes.Invoke(null, new object[] { obj });
                return bytesRet;
            }
        }
        

        所以 GetBytes 接受一个对象。然后它获取它的类型并尝试根据传入的对象类型从 BitConverter 获取 MethodInfo。如果它找不到接受该类型作为参数的重载,则会引发 InvalidOperation 异常。如果是,则调用它传入 obj 的实例作为值并返回字节数组。

        例如使用代码,

        //make sure the extensions namespace is defined where this code is run.
        Console.WriteLine(((ushort)255).GetBytes().ToBase64());
        Console.WriteLine(10.0.GetBytes().ToBase64());
        Console.WriteLine(((int)2000000000).GetBytes().ToBase64());
        Console.WriteLine(((short)128).GetBytes().ToBase64());
        //Below causes an error
        Console.WriteLine("cool".GetBytes().ToBase64()); //because BitConvert.GetBytes has no overload accepting an argument of type string.
        

        【讨论】:

          【解决方案6】:

          您需要使用反射来做到这一点。

          1. BitConverter 静态类型中获取GetBytes 方法组。
          2. 取出第一个参数类型为TSource的重载。
          3. 通过Invoke 方法调用该特定方法。

          如果您不熟悉其中的一些内容,我可以使用这些步骤的代码来扩展答案。

          编辑:或者像其他人建议的那样使用 dynamic 并为自己节省一些工作。

          【讨论】:

            【解决方案7】:

            您的代码无法编译,因为编译器无法验证TSource 的任何类型都将被BitConverter.GetBytes() 接受。您可以单独检查每种类型并进行转换:

            public byte[] GetBytes <TSource> (TSource input)
            {
                var t = typeof(TSource);
                return    (t == typeof(int))  ? BitConverter.GetBytes((int) (object) input)
                        : (t == typeof(bool)) ? BitConverter.GetBytes((bool)(object) input)
                        : (t == typeof(char)) ? BitConverter.GetBytes((char)(object) input)
                        : null;
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2010-11-15
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多