如何使用generics将参数传递给非generics方法?

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

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 input) { BitConverter.GetBytes(input); } } 

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

通常,您不能,除非有问题的方法将System.Object作为参数。 问题是generics不仅限于方法调用参数允许的类型。

您最接近的是使用运行时绑定:

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

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

这不起作用的原因是generics方法仍然静态地解析对其中的方法的调用。 由于TSource可以是任何类型,它只能在BitConverter上调用一个带有object参数的方法。 由于不存在,编译失败。

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

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

虽然通用参数现在是多余的,但您没有类型安全性。

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

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

或者取一个Func参数并包装你需要的每个BitConverter方法,例如

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

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

在代码调用BitConverter.GetBytes ,类型是TSource ,因此调用不能静态地绑定调用。 您可以使用动态调用来解决这个问题,这意味着它可以正常编译,然后在运行时得到解决:

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

您将为使用动态调用支付性能损失,如果没有合适的调用方法可用,您将获得运行时exception。

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

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

如果您愿意提高性能,可以使用reflection和对象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。如果它找不到接受该类型作为参数的重载,则抛出InvalidOperationexception。 如果是,则调用它将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. 

你需要使用reflection来做到这一点。

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

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

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

您的代码无法编译,因为编译器无法validationBitConverter.GetBytes()是否接受任何TSource类型。 您可以单独检查每种类型并进行投射:

 public byte[] GetBytes  (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; }