C#不能从泛型方法调用重载的非泛型方法

Iai*_*oat 16 c# generics reflection

我有一些遗留代码,方法foo有700多个重载:

[DllImport("3rdparty.dll")]
protected static extern void foo(int len, ref structA obj);
[DllImport("3rdparty.dll")]
protected static extern void foo(int len, ref structB obj);
[DllImport("3rdparty.dll")]
protected static extern void foo(int len, ref structC obj);
//and 700 similar overloads for foo...
Run Code Online (Sandbox Code Playgroud)

我想通过使用泛型的单个方法公开这些重载方法:

public void callFoo<T>(int len)
    where T : new()  //ensure an empty constructor so it can be activated
{
   T obj = Activator.CreateInstance<T>(); //foo expects obj to be empty, and fills it with data
   foo(len, ref obj);

   //...do stuff with obj...
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这会返回错误:" 'foo(int,ref StructA)'的最佳重载方法匹配'有一些无效的参数 "和" 不能从'ref T'转换为'ref StructA' ".

有没有一种优雅的方式来实现这一目标?

Mar*_*ell 8

我希望这dynamic会有所帮助,但它不喜欢ref.无论如何,反思应该有效:

public T callFoo<T>(int len)
    where T : new()  //ensure an empty constructor so it can be activated
{
   T obj = new T();
   GetType().GetMethod("foo", BindingFlags.Instance | BindingFlags.NonPublic,
       null,  new[] { typeof(int), typeof(T).MakeByRefType() }, null)
       .Invoke(this, new object[] { len, obj });
   return obj;
}
Run Code Online (Sandbox Code Playgroud)

这是一个只进行一次反射的优化版本; 应该快:

class Test
{

    protected void foo(int len, ref classA obj){}
    protected void foo(int len, ref classB obj){  }
    protected void foo(int len, ref classC obj){}
    static readonly Dictionary<Type, Delegate> functions;
    delegate void MyDelegate<T>(Test arg0, int len, ref T obj);
    static Test()
    {
        functions = new Dictionary<Type, Delegate>();
        foreach (var method in typeof(Test).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance))
        {
            if (method.Name != "foo") continue;
            var args = method.GetParameters();
            if (args.Length != 2 || args[0].ParameterType != typeof(int)) continue;
            var type = args[1].ParameterType.GetElementType();
            functions[type] = Delegate.CreateDelegate(
                typeof(MyDelegate<>).MakeGenericType(type), method);
        }
    }
    public T callFoo<T>(int len)
        where T : new()  //ensure an empty constructor so it can be activated
    {
        T obj = new T();
        Delegate function;
        if (!functions.TryGetValue(typeof(T), out function)) throw new NotSupportedException(
             "foo is not supported for " + typeof(T).Name);
        ((MyDelegate<T>)function)(this, len, ref obj);
        return obj;
    }
}
Run Code Online (Sandbox Code Playgroud)


Ita*_*aro 5

首先 - 因为你where T : new()
可以只是陈述T obj = new T();而不是T obj = Activator.CreateInstance<T>();
现在,对于另一个问题,在一个类中有很多这样的函数是混乱的.
我会定义一个接口

public interface IFoo
{
   void foo(int len);
}
Run Code Online (Sandbox Code Playgroud)

并使所有类实现它.然后:

public void callFoo<T>(int len)
    where T : IFoo, new()  //ensure an empty constructor so it can be activated
{
   T obj = new T();
   obj.foo(len);
}
Run Code Online (Sandbox Code Playgroud)


Han*_*ant 5

您可以通过自己处理编组而不是将其留给P/Invoke编组来完成此操作.Redeclare foo是这样的:

    [DllImport("3rdparty.dll")]
    private static extern void foo(int len, IntPtr obj);
Run Code Online (Sandbox Code Playgroud)

现在允许您定义通用方法:

    protected void foo<T>(ref T obj) {
        int len = Marshal.SizeOf(obj);
        IntPtr mem = Marshal.AllocCoTaskMem(len);
        try {
            Marshal.StructureToPtr(obj, mem, false);
            foo(len, mem);
            // Optional:
            obj = (T)Marshal.PtrToStructure(mem, typeof(T));
        }
        finally {
            Marshal.FreeCoTaskMem(mem);
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果perf是关键的,那么你可以通过保持AllocCoTaskMem分配的内存来加速它,只在需要时增长它.从您的问题不清楚C函数是否更新传递的结构,如果没有,您可以省略PtrToStructure调用.