简体   繁体   English

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

[英]C# cannot call overloaded non-generic method from generic method

I have some legacy code with a method foo which has 700+ overloads: 我有一些遗留代码,方法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...

I'd like to expose these overloaded methods through a single method using generics: 我想通过使用泛型的单个方法公开这些重载方法:

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

Unfortunately this returns the errors: " The best overloaded method match for 'foo(int, ref StructA)' has some invalid arguments " and " cannot convert from 'ref T' to 'ref StructA' ". 不幸的是,这会返回错误:“ 'foo(int,ref StructA)'的最佳重载方法匹配'有一些无效的参数 ”和“ 不能从'ref T'转换为'ref StructA' ”。

Is there an elegant way to achieve this? 有一种优雅的方式来实现这一目标吗?

I was hoping that dynamic would help here, but it doesn't like the ref . 我希望dynamic在这里有所帮助,但它不喜欢ref Anyway, reflection should work: 无论如何,反思应该有效:

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

Here's an optimized version that only does the reflection once; 这是一个只进行一次反射的优化版本; should be much faster: 很多

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

First - since you have where T : new() 首先 - 因为你有where T : new()
you can just state T obj = new T(); 你可以说T obj = new T(); instead of T obj = Activator.CreateInstance<T>(); 而不是T obj = Activator.CreateInstance<T>();
Now, for the other issue, have a lot of functions like this in one class is chaos. 现在,对于另一个问题,在一个类中有很多这样的函数是混乱的。
I would define an interface 我会定义一个接口

public interface IFoo
{
   void foo(int len);
}

and make all the classes implement it. 并使所有类实现它。 And then: 然后:

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

You can do this by taking care of the marshaling yourself instead of leaving it to the P/Invoke marshaller. 您可以通过自己处理编组而不是将其留给P / Invoke编组来完成此操作。 Redeclare foo like this: Redeclare foo是这样的:

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

Which now allows you to define a generic method: 现在允许您定义通用方法:

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

If perf is critical then you can speed it up by keeping the memory allocated by AllocCoTaskMem around, growing it only if needed. 如果perf是关键的,那么你可以通过保持AllocCoTaskMem分配的内存来加速它,只在需要时增长它。 It isn't clear from your question whether the C functions updates the passed structure, you can omit the PtrToStructure call if it doesn't. 从您的问题不清楚C函数是否更新传递的结构,如果没有,您可以省略PtrToStructure调用。

I'm afraid that you cannot use generics in a way you want to here. 我担心你不能以你想要的方式使用泛型。 The reason is that the generic method needs to be compiled to IL and it needs to resolve the overload at compile time . 原因是泛型方法需要编译为IL,并且需要在编译时解决重载。 At that point, it doesn't really know which overload to choose, because this is runtime information. 此时,它并不真正知道要选择哪个重载,因为这是运行时信息。

If you have as many overloads as you say, then I would really consider using some better abstraction. 如果你有多少重载,那么我真的会考虑使用一些更好的抽象。 For example, implement your foo method as a member of some interface that is implemented by all the classes. 例如,将foo方法实现为由所有类实现的某个接口的成员。 If you provide more details, I'm sure people here can give advice on better design. 如果您提供更多详细信息,我相信这里的人可以就更好的设计提供建议。

If you really need to do it this way, then you could probably use something like Dictionary<Type, SomeDelegate<int, obj> and store all foo methods in a dictionary. 如果你真的需要这样做,那么你可能会使用像Dictionary<Type, SomeDelegate<int, obj> ,并将所有foo方法存储在字典中。 The callFoo method would simply perform a lookup: callFoo方法只会执行查找:

public void callFoo<T>(int len)  where T : new()
{ 
   T obj = Activator.CreateInstance<T>();
   fooDictionary[typeof(T)](len, obj);
   // ...
} 

Then the only problem would be, how to add all of them to the dictionary. 那么唯一的问题是,如何将它们全部添加到字典中。 You can probably do that simply by hand, in static constructor of each class or dynamically using reflection. 您可以在每个类的静态构造函数中手动执行此操作,也可以使用反射动态执行此操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM