Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何对运行时已知的类型执行方法重载解析?_C#_Generics_Reflection_Runtime_Overloading - Fatal编程技术网

C# 如何对运行时已知的类型执行方法重载解析?

C# 如何对运行时已知的类型执行方法重载解析?,c#,generics,reflection,runtime,overloading,C#,Generics,Reflection,Runtime,Overloading,我有一个包含方法重载的类: public static void Foo(string a, string b) public static void Foo(DateTime a, DateTime b) public static void Foo<T>(ICollection<T> a, T b) public static void Foo<T>(T a, ICollection<T> b) public static void Foo&l

我有一个包含方法重载的类:

public static void Foo(string a, string b)
public static void Foo(DateTime a, DateTime b)
public static void Foo<T>(ICollection<T> a, T b)
public static void Foo<T>(T a, ICollection<T> b)
public static void Foo<T>(ICollection<T> a, ICollection<T> b)
public static void Foo<T, U>(T a, U b) where T : IComparable
public static void Foo<T, U>(T a, U b)
对于两种给定的参数类型(例如:
typeof(int)
typeof(List)
),我希望动态选择最合适的方法。

到目前为止我尝试/检查的内容:

  • Binder.SelectMethod()
    :该方法不支持泛型
  • typeof().IsAssignableFrom()
    :同上。对泛型参数有一些支持(例如:提供泛型参数
    T
    ,它对类型
    int
    的约束将得到正确的评估),但不支持带有参数的泛型类型(例如:
    List
    vs
    List
    或vs
    List
    (此类型来自泛型方法,可能
    T
    包含一些约束)

  • 使用所有泛型方法的输入参数调用
    MakeGenericMethod()
    ,并捕获参数异常,直到没有异常为止:丑陋。 它可能需要做一些额外的工作,因为我将提供给
    MakeGenericMethod()
    的类型可能与我收到的初始类型不完全相同(例如:我有
    typeof(List),typeof(int)
    ,而我应该提供
    typeof(int)
    MakeGenericMethod()

我知道这是一个复杂的话题,不是一个琐碎的问题,但有一些事情是固定的,应该让事情变得更容易:

  • 所有方法都有两个参数和相同的返回类型
  • 没有可选类型,没有数组
  • 泛型方法不再有一个或两个泛型参数,参数的顺序始终相同
  • 不需要支持协方差/逆变
  • 方法已按“优先级”排序。这就是为什么没有约束的泛型方法位于此列表的末尾。 一旦一种方法匹配,我们就可以接受它,而忽略其他方法

我想知道是否有办法使用C#Framework中的现有类/方法解决这些重载问题(无需重新发明轮子),如果不可能,可能的解决方案是什么(自定义代码)

考虑到您描述的先决条件,您可以这样做:

static void ExecuteCase(object a, object b)
{
    var method = GetMethod(typeof(Bar).GetMethods(BindingFlags.Public | BindingFlags.Static), a.GetType(), b.GetType());
    if (method != null) { method.Invoke(null, new object[] {a, b}); }
    else { /* Handle method not found */ }
}
其中
GetMethod
是:

private static MethodInfo GetMethod(IEnumerable<MethodInfo> methods, Type typeA, Type typeB)
{
    foreach (var method in methods)
    {
        List<MethodInfo> candidates = new List<MethodInfo>();
        if (!method.IsGenericMethod) { candidates.Add(method); }
        else
        {
            MethodInfo genericMethod;
            if (IsParameterOfGeneric(typeA, typeB) && TryMakeGenericMethod(method, typeB, typeB, out genericMethod)) candidates.Add(genericMethod);
            if (IsParameterOfGeneric(typeB, typeA) && TryMakeGenericMethod(method, typeA, typeA, out genericMethod)) candidates.Add(genericMethod);
            if (typeA.IsGenericType && typeB.IsGenericType && TryMakeGenericMethod(method, typeA.GetGenericArguments()[0], typeB.GetGenericArguments()[0], out genericMethod)) candidates.Add(genericMethod);
            if (TryMakeGenericMethod(method, typeA, typeB, out genericMethod)) candidates.Add(genericMethod);
        }

        foreach (var candidate in candidates)
        {
            var args = candidate.GetParameters();
            if (args[0].ParameterType.IsAssignableFrom(typeA) && args[1].ParameterType.IsAssignableFrom(typeB)) return candidate;
        }
    }

    return null;
}

private static bool IsParameterOfGeneric(Type generic, Type parameter)
{
    if (!generic.IsGenericType) return false;
    return generic.GetGenericArguments()[0] == parameter;
}

private static bool TryMakeGenericMethod(MethodInfo method, Type typeA, Type typeB, out MethodInfo genericMethod)
{
    genericMethod = null;
    try
    {
        genericMethod = method.GetGenericArguments().Length == 1
            ? method.MakeGenericMethod(typeA)
            : method.MakeGenericMethod(typeA, typeB);

        return true;
    }
    catch (ArgumentException ex)
    {
        if (ex.Message.Contains("violates the constraint of type")) return false;
        throw;
    }
}
private static MethodInfo GetMethod(IEnumerable方法,类型A,类型B)
{
foreach(方法中的var方法)
{
列表候选者=新列表();
如果(!method.IsGenericMethod){candidates.Add(method);}
其他的
{
方法信息一般方法;
如果(IsParameterOfGeneric(typeA,typeB)和&TryMakeGenericMethod(method,typeB,typeB,out genericMethod))候选。添加(genericMethod);
如果(IsParameterOfGeneric(typeB,typeA)和&TryMakeGenericMethod(method,typeA,typeA,out genericMethod))候选项。添加(genericMethod);
如果(typeA.IsGenericType&&typeB.IsGenericType&&TryMakeGenericMethod(方法,typeA.GetGenericArguments()[0],typeB.GetGenericArguments()[0],out genericMethod))候选。添加(genericMethod);
if(trymakenergicmethod(method,typeA,typeB,out-genericMethod))候选者。Add(genericMethod);
}
foreach(候选人中的var候选人)
{
var args=candidate.GetParameters();
if(args[0].ParameterType.IsAssignableFrom(typeA)和&args[1].ParameterType.IsAssignableFrom(typeB))返回候选者;
}
}
返回null;
}
私有静态bool IsParameterOfGeneric(类型泛型,类型参数)
{
如果(!generic.IsGenericType)返回false;
返回generic.GetGenericArguments()[0]==参数;
}
私有静态bool-trymakenericmethod(MethodInfo方法,typeA类型,typeB类型,out-MethodInfo-genericMethod)
{
genericMethod=null;
尝试
{
genericMethod=method.GetGenericArguments().Length==1
?方法。MakeGenericMethod(类型A)
:方法.MakeGenericMethod(typeA,typeB);
返回true;
}
捕获(参数异常)
{
if(例如Message.Contains(“违反类型的约束”))返回false;
投掷;
}
}

仅供参考,

谢谢你的回答。无论如何,签名
ExecuteCase
方法在我看来并不好,因为它使用泛型参数,这意味着我知道输入类型是运行时(我不知道操作的全部要点)。另外:如果输入类型是
List,int
,匹配的方法是
Foo(List a,tb)
,那么MakeGenericMethod try-and-catch技术是否有效?(那么泛型参数应该是
int
而不是
List
int
)。请参阅编辑。修复了上面评论中提到的案例问题。还将
ExecuteCase
从泛型更改为
object
。我使用了一种自定义解决方案,其实现与microsoft的
IsAssignableFrom()
方法非常类似:区别在于没有直接检查类型是否相等(例如:
this==c
)它使用了一个自定义方法来处理泛型(例如:
typeof(List==typeof(List)
将是真的),它工作得非常好。如果我有时间,我会发布解决方案(在清理之后)。
private static MethodInfo GetMethod(IEnumerable<MethodInfo> methods, Type typeA, Type typeB)
{
    foreach (var method in methods)
    {
        List<MethodInfo> candidates = new List<MethodInfo>();
        if (!method.IsGenericMethod) { candidates.Add(method); }
        else
        {
            MethodInfo genericMethod;
            if (IsParameterOfGeneric(typeA, typeB) && TryMakeGenericMethod(method, typeB, typeB, out genericMethod)) candidates.Add(genericMethod);
            if (IsParameterOfGeneric(typeB, typeA) && TryMakeGenericMethod(method, typeA, typeA, out genericMethod)) candidates.Add(genericMethod);
            if (typeA.IsGenericType && typeB.IsGenericType && TryMakeGenericMethod(method, typeA.GetGenericArguments()[0], typeB.GetGenericArguments()[0], out genericMethod)) candidates.Add(genericMethod);
            if (TryMakeGenericMethod(method, typeA, typeB, out genericMethod)) candidates.Add(genericMethod);
        }

        foreach (var candidate in candidates)
        {
            var args = candidate.GetParameters();
            if (args[0].ParameterType.IsAssignableFrom(typeA) && args[1].ParameterType.IsAssignableFrom(typeB)) return candidate;
        }
    }

    return null;
}

private static bool IsParameterOfGeneric(Type generic, Type parameter)
{
    if (!generic.IsGenericType) return false;
    return generic.GetGenericArguments()[0] == parameter;
}

private static bool TryMakeGenericMethod(MethodInfo method, Type typeA, Type typeB, out MethodInfo genericMethod)
{
    genericMethod = null;
    try
    {
        genericMethod = method.GetGenericArguments().Length == 1
            ? method.MakeGenericMethod(typeA)
            : method.MakeGenericMethod(typeA, typeB);

        return true;
    }
    catch (ArgumentException ex)
    {
        if (ex.Message.Contains("violates the constraint of type")) return false;
        throw;
    }
}