当调用类本身是通用的时,是否可以强制外部类调用专用的重载?

Bil*_*rey 7 c# generics

我正在编写与外部API接口的代码,我无法更改:

public class ExternalAPI
{
    public static void Read(byte[] buffer);
    public static void Read(int[] buffer);
    public static void Read(float[] buffer);
    public static void Read(double[] buffer);
}
Run Code Online (Sandbox Code Playgroud)

在读取数据时调用正确的重载方法很重要buffer,但是一旦读入数据,我将一般地处理它.我在代码中的第一次传递是:

public class Foo<T>
{
    T[] buffer;

    public void Stuff()
    {
        ExternalAPI.Foo(buffer);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,C#不会转换T[]byte[].有没有办法枚举T可以明确表示的类型?我尝试过使用一个where T :条款,但似乎没有办法说出来where T : {byte, int, float, double and nothing else ever}

按照这里的建议:匹配数字类型的通用约束,我向泛型添加了约束,并且还为我的模拟API添加了一个泛型方法,该方法将object参数作为参数

public class ExternalAPI
{
    public static void Read(object buffer);
    public static void Read(byte[] buffer);
    public static void Read(int[] buffer);
    public static void Read(double[] buffer);
}

public class Foo<T> where T: struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
{
    T[] buffer;

    public void Stuff()
    {
        ExternalAPI.Read(buffer);
    }
}
Run Code Online (Sandbox Code Playgroud)

这将编译并愉快地运行,但是唯一被调用的方法是Foo(object buffer),即使Tbyte.当调用类是通用的时,有没有办法强制非泛型类的方法使用最具体的重载?

Jep*_*sen 2

当调用类是泛型时,有没有办法强制非泛型类的方法使用最具体的重载?

通常,重载决策发生在编译时。由于 上的约束T(即 )where T: struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable不能指向除接受 的重载之外的任何其他重载object,因此这就是被调用的重载。

如果您愿意使用dynamic,重载决策将在运行时发生。只需将参数转换为dynamic,如下所示:

public void Stuff()
{
  ExternalAPI.Read((dynamic)buffer);
}
Run Code Online (Sandbox Code Playgroud)

这样做的坏处是速度较慢,因为必须在程序运行时设置重载解析。但如果是、等T之一,则将调用 等的相应重载。如果是不支持的东西,并且如果中不存在重载,则这将在运行时失败,并引发异常,但直到该方法运行为止。byteintbyte[]int[]TobjectExternalAPIStuff

另一个解决方案是为您的Foo<T>类配备一个委托来保存正确的方法。它可能是这样的:

T[] buffer;

readonly Action<T[]> readMethod;

public void Stuff()
{
  readMethod(buffer);
}

//constructor
public Foo(Action<T[]> readMethod)
{
  this.readMethod = readMethod;
}
Run Code Online (Sandbox Code Playgroud)

但是人们必须像这样实例化你的类:

new Foo<byte>(ExternalAPI.Read)
Run Code Online (Sandbox Code Playgroud)

正确的重载将在人们创建Foo<>. 您可以在实例构造函数中添加检查,该实例构造readMethod函数确实是一个单播委托,表示Read由 定义的调用方法typeof(ExternalAPI)

第三种解决方案是创建该readMethod字段static,并包含一个用于初始化的静态构造函数readMethod。这看起来很难看,但静态构造函数只会为您使用的每种类型( 、 等)运行byte一次int。如果人们使用了错误的 ,静态构造函数可能会抛出异常T。静态构造函数可能如下所示:

static Foo()
{
  if (typeof(T) == typeof(byte))
    readMethod = (Action<T[]>)(Delegate)(Action<byte[]>)ExternalAPI.Read;
  else if (typeof(T) == typeof(int))
    readMethod = (Action<T[]>)(Delegate)(Action<int[]>)ExternalAPI.Read;
  else if (typeof(T) == typeof(float))
    readMethod = (Action<T[]>)(Delegate)(Action<float[]>)ExternalAPI.Read;
  else if (typeof(T) == typeof(double))
    readMethod = (Action<T[]>)(Delegate)(Action<double[]>)ExternalAPI.Read;
  else
    throw new Exception("The type parameter T can't be " + typeof(T));
}
Run Code Online (Sandbox Code Playgroud)

编辑:受到下面第一条评论的启发,这里尝试让静态构造函数使用反射:

static Foo()
{
  var methInfo = typeof(ExternalAPI).GetMethod("Read", new[] { typeof(T[]), });
  if (methInfo == null)
    throw new Exception("ExternalAPI has no suitable method for " + typeof(T[]));
  readMethod = (Action<T[]>)Delegate.CreateDelegate(typeof(Action<T[]>), methInfo);
}
Run Code Online (Sandbox Code Playgroud)