当预期为 c# 中的泛型接口基类型时,如何传递方法参数

本文关键字:类型 何传递 参数 方法 基类 泛型接口 | 更新日期: 2023-09-27 18:34:02

>假设我有一个定义如下的接口:

interface IContract
{
    void CommonMethod();
}

然后是另一个接口,它继承自此接口,定义方式如下:

interface IContract<T> : IContract where T : EventArgs
{
    event EventHandler<T> CommonEvent;
}


我的具体问题是,给定任何实现IContract的实例,我如何确定是否也IContract<T>,如果是,IContract<T>的通用类型是什么,而无需对我可能遇到的每种已知类型的IContract<T>进行硬编码。


最终,我将使用此确定来调用以下模式:

void PerformAction<T>(IContract<T> contract) where T : EventArgs
{
    ...
}

当预期为 c# 中的泛型接口基类型时,如何传递方法参数

由于您需要IContract<T>的实例,因此必须首先使用反射来获取泛型类型参数,然后调用适当的方法:

// results in typeof(EventArgs) or any class deriving from them
Type type = myContract.GetType().GetGenericArguments()[0]; 

现在获取IContract<T>的泛型类型定义并获取适当的方法。

// assuming that MyType is the type holding PerformAction
Type t = typeof(MyType).MakeGenericType(type); 
var m = t.GetMethod("PerformAction");

或者,如果只有方法PerforAction是泛型的而不是MyType

// assuming that MyType is the type holding PerformAction
Type t = typeof(MyType); 
var m = t.GetMethod("PerformAction").MakeGenericMethod(type);

现在你应该能够在 IContract 的实例上调用该方法:

var result = m.Invoke(myInstance, new[] { myContract } );

其中myInstance属于类型 MyType

这是我

使用的解决方案:

首先,非常感谢@HimBromBeere提供此解决方案的基础。 从本质上讲,他发布的是答案,但是这里包括我如何实现此体系结构的相关细节。

给定以下接口:

interface IContract
{
    void CommonMethod();
}
interface IContract<T> : IContract where T : EventArgs
{
    event EventHandler<T> CommonEvent;
}

我有一个类,出于所有意图和目的,声明如下:

abstract class BaseControl
{
    protected void SubscribeToContract<Type>(IContract<Type> contract) 
        where Type : EventArgs
    {
        contract.ContractChanged += 
            delegate(object sender, Type e)
            {
                // Calls this method to perform other actions:
                OnContractChanged(e);
            };
    }
    // Called when IContract<>.ContractChanged is raised:
    protected virtual void OnContractChanged(EventArgs e);
}
class SpecialControl : BaseControl, IContract<SpecialEventArgs>
{
    ...
    protected override OnContractChanged(EventArgs e)
    {
        base.OnContractChanged();
        PerformAction(e);
    }
    ...
    void PerformAction(EventArgs e)
    {
        // We can also now find out if "e" is SpecialEventArgs
        if (e is SpecialEventArgs)
        {
            ...
        }
        ...
    }
   ...      
}

其中方法SubscribeToContract<Type>()是从BaseControl继承的受保护成员。 这允许派生类实现自己的方法,如PerformAction(),同时抽象出不得不担心处理通用接口的东西(目标(。

在我的类中有一个event,我使用以下语句来确定参数是否实现了IContract,这是IContract<>的基本接口:

if (e.Control is IContract)
{
    // Gets all the IContract<> interfaces e.Control implements:
    var generics = GetIContractGenerics(e.Control.GetType());
    // There might be multiple IContract<> implementations
    // so therefore we need to check them all, even though in
    // practice there will likely only be a single instance:
    foreach (var generic in generics)
    {
        var method = this.GetType().GetMethod(
            "SubscribeToContract", (BindingFlags.Instance | BindingFlags.NonPublic));
        method = method.MakeGenericMethod(generic);
        var result = method.Invoke(this, new[] { e.Control });
    }
}

在我的情况下,需要设置(BindingFlags.Instance | BindingFlags.NonPublic)才能让GetMethod()函数返回SubscribeToContract<Type>()方法。

其中方法GetIContractGenerics()是使用此 StackOverflow 帖子中描述的方法创建的:

protected static IEnumerable<Type> GetIContractGenerics(Type type)
{
    return
        from implementation in type.GetInterfaces()
        where implementation.IsGenericType
        let definition = implementation.GetGenericTypeDefinition()
        where definition == typeof(IContract<>)
        select implementation.GetGenericArguments().First();
}

为方便起见,此GetIContractGenerics()静态方法存储在BaseControl中。

就是这样,一切正常! 感谢大家的投入!