簡體   English   中英

C#不能從泛型方法調用重載的非泛型方法

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

我有一些遺留代碼,方法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...

我想通過使用泛型的單個方法公開這些重載方法:

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

不幸的是,這會返回錯誤:“ 'foo(int,ref StructA)'的最佳重載方法匹配'有一些無效的參數 ”和“ 不能從'ref T'轉換為'ref StructA' ”。

有一種優雅的方式來實現這一目標嗎?

我希望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;
}

這是一個只進行一次反射的優化版本; 很多

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

首先 - 因為你有where T : new()
你可以說T obj = new T(); 而不是T obj = Activator.CreateInstance<T>();
現在,對於另一個問題,在一個類中有很多這樣的函數是混亂的。
我會定義一個接口

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

並使所有類實現它。 然后:

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

您可以通過自己處理編組而不是將其留給P / Invoke編組來完成此操作。 Redeclare foo是這樣的:

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

現在允許您定義通用方法:

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

如果perf是關鍵的,那么你可以通過保持AllocCoTaskMem分配的內存來加速它,只在需要時增長它。 從您的問題不清楚C函數是否更新傳遞的結構,如果沒有,您可以省略PtrToStructure調用。

我擔心你不能以你想要的方式使用泛型。 原因是泛型方法需要編譯為IL,並且需要在編譯時解決重載。 此時,它並不真正知道要選擇哪個重載,因為這是運行時信息。

如果你有多少重載,那么我真的會考慮使用一些更好的抽象。 例如,將foo方法實現為由所有類實現的某個接口的成員。 如果您提供更多詳細信息,我相信這里的人可以就更好的設計提供建議。

如果你真的需要這樣做,那么你可能會使用像Dictionary<Type, SomeDelegate<int, obj> ,並將所有foo方法存儲在字典中。 callFoo方法只會執行查找:

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

那么唯一的問題是,如何將它們全部添加到字典中。 您可以在每個類的靜態構造函數中手動執行此操作,也可以使用反射動態執行此操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM