【问题标题】:How to pass method parameter when expected to be generic interface base type in c#?c# - 当期望是通用接口基类型时如何传递方法参数?
【发布时间】:2016-05-20 11:10:49
【问题描述】:

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

interface IContract
{
    void CommonMethod();
}

然后是继承自该接口的另一个接口,其定义方式为:

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


我的具体问题是,给定任何实现IContract 的实例,我如何确定是否也是IContract&lt;T&gt;,如果是,那么IContract&lt;T&gt; 的泛型类型是什么?编码我可能遇到的每种已知类型的IContract&lt;T&gt;


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

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

【问题讨论】:

  • 即使在可能的情况下,如果仅在编译时知道泛型类型参数,您将如何调用泛型方法?您是否打算将IContract 的实例转换为IContract&lt;WhatEver&gt;
  • @HimBromBeere 是的,这就是计划。
  • 谁说它没有为多个 T 实现 IContract&lt;T&gt;?应该使用哪个实现?全部?

标签: c# winforms generics


【解决方案1】:

由于您需要IContract&lt;T&gt; 的实例,您必须先使用反射来获取泛型类型参数,然后再调用适当的方法:

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

现在获取IContract&lt;T&gt; 的通用类型定义并获取适当的方法。

// 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

【讨论】:

  • 很好,我没想到!不幸的是,我无法利用它,因为实现接口的类也是具有设计时支持的 Windows 控件。泛型类声明(class Bar&lt;T&gt;)与 Visual Studio 设计器不兼容。
  • 在后续编辑中使用反射看起来很有希望。我现在正在尝试集成到测试解决方案中。
  • 能够使用此答案中描述的方法获得解决方案。有关详细信息/实际实施,请参阅下面的答案。谢谢!
【解决方案2】:

这是我用过的解决方案:

首先,非常感谢@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&lt;Type&gt;() 是从BaseControl 继承的受保护成员。这允许派生类实现自己的方法,例如PerformAction(),同时抽象出不必担心处理通用接口的东西(目标)。

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

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&lt;Type&gt;() 方法。

其中方法GetIContractGenerics()是使用this StackOverflow post中描述的方法创建的:

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 中。

就是这样,一切正常!感谢大家的意见!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-03
    • 2019-12-24
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多