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

时间:2014-04-23 18:06:32

标签: c# generics

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

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

7 个答案:

答案 0 :(得分:8)

  

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

一般情况下,您不能,除非相关方法将System.Object作为参数。问题是泛型不仅限于方法调用参数允许的类型。

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

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

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

答案 1 :(得分:1)

这不起作用的原因是泛型方法仍然静态地解析对其中的方法的调用。由于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<T, byte[]>参数并包装您需要的每个BitConverter方法,例如

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

可能会给你更大的灵活性。

答案 2 :(得分:1)

在代码调用BitConverter.GetBytes的情况下,类型为TSource,因此调用无法通过编译器进行静态绑定。您可以使用动态调用解决此问题,这意味着它可以正常编译,然后在运行时解析:

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

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:1)

如果您愿意受到性能影响,可以使用反射和对象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实例作为值传递并返回字节数组。

E.g。使用代码,

//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.

答案 5 :(得分:0)

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

  1. GetBytes静态类型获取BitConverter方法组。
  2. 拉出第一个参数类型为TSource
  3. 的重载
  4. 通过Invoke方法调用该特定方法。
  5. 如果您对某些内容不熟悉,我可以使用这些步骤的代码扩展答案。

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

答案 6 :(得分:0)

您的代码无法编译,因为编译器无法验证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;
}