【问题标题】:How to store a list of different generic things in C#如何在 C# 中存储不同通用事物的列表
【发布时间】:2017-02-16 16:12:06
【问题描述】:

比如说我有这个基类

class Mangler<TInput, TOutput>
{
}

然后我创建了几个派生类

class StringToBytesMangler : Mangler<string, byte[]>
{
}

class IntToGuidMangler : Mangler<int, Guid>
{
}

如何存储Mangler&lt;TInput, TOutput&gt; 的集合,其中TInputTOutput 在任何给定时间都可能不同?

List<Mangler<?, ?>> list = new List<Mangler<?, ?>>();

list.Add(new StringToBytesMangler());
list.Add(new IntToGuidMangler());

这可能吗?

【问题讨论】:

  • Mangler&lt;string,byte[]&gt;Mangler&lt;int,Guid&gt; 是完全不同的类型,因此保留它们的唯一通用列表可能是 List&lt;object&gt;。也许您可以从另一个基类或接口派生Mangler&lt;TInput,TOutput&gt;,并将该类型用于列表。
  • @AlexB。很好的评论,虽然 stringintbyte[]Guid 是一些不相关的类型。

标签: c# generics collections


【解决方案1】:

您需要一个非泛型 Mangler 基类。

List<Mangler> list = new List<Mangler>();

list.Add(new StringToBytesMangler());
list.Add(new IntToGuidMangler());

当然,这意味着您还需要拥有依赖于TInputTOutput 的方法的非泛型版本。

【讨论】:

  • object
  • @Sinatr,是的,但这很丑,而且它不允许您定义公开类功能的方法。
【解决方案2】:

如果我正确理解了这个问题,那么您尝试的方式是不可能的。 StringToBytesManglerIntToGuidMangler 类型不是派生自同一个类型。您可以引入共享基类型,但我建议重新考虑设计 - 即使它们可以存储在同一个集合中,它们在语法上也没有任何共同点(至少问题中没有显示)。

【讨论】:

    【解决方案3】:

    泛型背后的整个想法是拥有泛型代码,因此该类的类型可以被视为相同。从您发布的内容中,很难看出您拥有哪些通用代码。

    下面我有一个包含一些通用代码的类:

    class Mangler<TInput, TOutput> 
       where TInput: ITInput 
       where TOutput: ITOutput {
    
       public TInput Input { get; set; }
       public TOutput Output { get; set; }
       public bool IsInputAGuid() {
          if (Guid.Parse(this.Input.SomeThing) == this.Output.SomeGuid ) {
             return true;
          }
    
          return false;
       }
    }
    

    你可以在上面的类中看到,当它把一个字符串从this.Input.Something解析成Guid,然后用this.Ouput.SomeGuid对它执行==,编译器很高兴,因为我们已经做出了约束: TInput 必须实现接口 ITInput 以便编译器知道该行将起作用并且Input 将具有Something 作为string 属性:

    Guid.Parse(this.Input.SomeThing)
    

    只要Something 可用,编译器就不会关心具体类型是什么。 TOuput 的想法相同,但编译器希望它实现 ITOutput,因此它希望 SomeGuid 中的 Guid。这就是为什么编译器乐于将字符串解析为 guid,然后对其执行 == 操作符,而另一件事也是 Guid

    以下是接口和一些实现它们的类:

    internal interface ITInput {
       string SomeThing { get; set; }
    }
    
    internal interface ITOutput {
       Guid SomeGuid { get; set; }
    }
    
    internal class AnotherInput : ITInput {
       public string SomeThing { get; set; }
    }
    
    internal class SomeInput : ITInput {
       public string SomeThing { get; set; }
    }
    
    internal class SomeOutput : ITOutput {
       public Guid SomeGuid { get; set; }
    }
    
    internal class SomeOtherOutput : ITOutput {
       public Guid SomeGuid { get; set; }
    }
    

    最后,这里是我们可以通用处理这些的用法:

    var manglers = new List<Mangler<ITInput, ITOutput>>();
    
    manglers.Add( new Mangler<ITInput, ITOutput>
    { Input = new SomeInput(), Output = new SomeOutput()  } );
    
    manglers.Add( new Mangler<ITInput, ITOutput>
    { Input = new AnotherInput(), Output = new SomeOutput() } );
    
    foreach( var thisMangler in manglers ) {
       var input = thisMangler.Input;
       var output = thisMangler.Output;
       var success = thisMangler.IsInputAGuid();
    }
    

    您可以在foreach 中看到,无论具体类型如何,我们都可以调用InputOutputIsInputAGuid()

    所以在你的代码中找到什么代码是通用的,然后将上面的技术应用到它上面。您可以为约束使用接口或基类。

    【讨论】:

    • 所以整个想法是使用界面。我理解正确吗?
    • 具有通用代码的接口或基类,因此您可以将对象视为相同,因为它们共享一个通用接口。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-26
    • 2017-05-12
    • 1970-01-01
    • 2012-02-29
    相关资源
    最近更新 更多