【问题标题】:C# Passing generics argument type at runtimeC# 在运行时传递泛型参数类型
【发布时间】:2012-12-10 08:22:15
【问题描述】:

在我的应用程序中,我有使用输入和输出相互链接的组件。将要交换的数据可以是任何类型,所以我使用泛型。

public interface IInputVariable<T>
{
    IOutputVariable<T> Source { get; set; }
}


public interface IOutputVariable<T>
{
    T Value { get; set; }
}

在另一个类中,组件链接在一起。哪些组件被链接,是从一个文件派生的。进行链接的类不知道输入和输出交换的类型。这个类只想用下面的代码把它们连接起来:

IOutputVariable<double> output;
Type argumentType = output.GetType().GetGenericArguments()[0];
IInputVariable<argumentType> input = new BasicInputVariable<argumentType>();
input.Source = output;

此代码无法编译,因为 argumentType 不能用作通用参数。有没有正确的方法来制定这个?或者是否可以在不知道其参数类型的情况下声明一个泛型变量?

【问题讨论】:

  • object 用于类型会有帮助吗? object 是你能想象到的最通用的类​​型。
  • 如果您的泛型参数在编译时未知,则不要使用泛型。否则,您将被困在笨拙且难以使用的类型中,而使用泛型不会带来任何好处。
  • 在程序的其他部分,泛型参数是已知的,并且泛型在那里非常有用。
  • 根据您的代码示例,输入和输出的类型必须相同,因为两个接口使用相同的 T 值。
  • 其实“IList”并没有实现IList。

标签: c# generics types interface linked-list


【解决方案1】:

设置Source 属性的最简单方法是在BasicInputVariable&lt;T&gt; 中创建接受IOutputVariable&lt;T&gt; 的counstructor:

public BasicInputVariable(IOutputVariable<T> source)
{
    Source = source;
}

然后你可以很容易地实例化它:

IOutputVariable<double> output;
Type argumentType = output.GetType().GetGenericArguments()[0];
object input = Activator.CreateInstance(typeof(BasicInputVariable<>)
                                       .MakeGenericType(argumentType), output);

另一种方法是创建并实现一个可以提供Source属性的接口:

public interface ISetSource
{
    object Source { get; set; }
}

public class BasicInputVariable<T> : IInputVariable<T>, ISetSource
{
    public IOutputVariable<T> Source { get; set; }

    object ISetSource.Source
    {
        get { return Source; }
        set { Source = (IOutputVariable<T>) value; }
    }
}

现在您可以访问Source 属性:

IOutputVariable<double> output;
var input = (ISetSource) Activator.CreateInstance(typeof (BasicInputVariable<>)
                                                 .MakeGenericType(argumentType));
input.Source = output;

使用 in/out 泛型参数可能更安全,但不幸的是我们不能将它们与值类型一起使用...

【讨论】:

    【解决方案2】:

    注意:这是非常丑陋的hack,不推荐..

    如果您有 .NET 4.0:

    IOutputVariable<double> output = something;
    dynamic input = Activator
                    .CreateInstance(typeof(BasicInputVariable<>)
                                    .MakeGenericType(output
                                                     .GetType()
                                                     .GetGenericArguments()[0]));
    //or may be:
    //dynamic input = typeof(BasicInputVariable<>)
    //                .MakeGenericType(output
    //                                 .GetType()
    //                                 .GetGenericArguments()[0])
    //                .GetConstructor(new Type[0])
    //                .Invoke(new object[0]);
    
    input.Source = output;
    

    但我不明白是什么阻止您将类型参数 double 本身直接传递给您的 BasicInputVariable 构造函数?

    IOutputVariable<double> output = something;
    IInputVariable<double> input = new BasicInputVariable<double>();
    input.Source = output;
    

    如果类型参数可以是任何东西,那么为什么不让整个方法通用呢?可能是:

    void Method<T>()
    {
        IOutputVariable<T> output = something;
        IInputVariable<T> input = new BasicInputVariable<T>();
        input.Source = output;
    }
    

    这很有效,因为您的 T 输入和输出变量都相同..


    如果没有更多细节,很难想出更好的解决方案,但认真地说,您首先应该重新考虑您的设计。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-13
      • 2016-05-11
      • 1970-01-01
      • 1970-01-01
      • 2014-10-04
      • 2019-02-27
      • 1970-01-01
      相关资源
      最近更新 更多