'Strongly Typed' 包含任意 <T> 个给定 Interface/Class 的通用集合

'Strongly Typed' Generic Collections that hold any <T> of a given Interface/Class

是否可以声明一个通用集合以仅保存实现具有任何通用接口的对象<T>

我的问题归结为:如果我想 to/have 存储实现通用接口的对象,是否有比使用非通用集合或([=15= 的通用)更好的方式来表达该事实]).

示例:

// An example Generic Interface
interface ISyncInterface<T>
{
    Task DoSync();
    IEnumerable<T> NewItems { get; }
}

// a manager-class that registers different classes implementing
// the generic interface.
// The code works - can it be done better?
class Manager
{
    private List<Object> _services = new List<Object>(); // <- works but is basically non generic
    // however the RegisterService() ensures that only correct types can be added.

    // would like to have something like below to indicate the Interface-Type
    // however: this would only allow _services2.Add to hold types of ISyncInterface<Object>
    //    - ISyncInterface<ServiceA_DTO> would fail.
    private List<ISyncInterface<Object>> _services2 = new List<ISyncInterface<Object>>();

    void RegisterService<T, U>(T service)
        where T : ISyncInterface<U>
    {
        _services.Add(service); // <- works e.g. for SyncServiceA 

        // _services2.Add(service); // <- FAILS for SyncServiceA - no conversion
        // _services2.Add((ISyncInterface<Object>) service); // <- FAILS also - no explicit cast
    }
}

// SETUP - The classes used above. Just to clarify.
class ServiceA_DTO { }
class ServiceB_DTO { }

class SyncServiceA : ISyncInterface<ServiceA_DTO>
{
    public Task DoSync() {}
    public IEnumerable<ServiceA_DTO> NewItems { get; }
}

class SyncServiceB : ISyncInterface<ServiceB_DTO>
{
    public Task DoSync() {}
    public IEnumerable<ServiceB_DTO> NewItems { get; } 
}

这可能吗?非常感谢任何建议!

更新:新的、更详细的代码来阐明问题。

下面有人建议将通用接口基于非通用接口。但结果是,所有实现通用接口的 类 都必须实现非通用方法、属性等 - 或者有没有办法绕过它?

感谢您的意见!

也许你可以尝试这样的事情:

  public interface MyInterface
  {//methods common to all types
    void FirstMethod();
  }
  public interface MyInterface<T> : MyInterface
  {//methods specific to a type
    void FirstMethod(T parameter);
  }
  public class MyClassThatHandlesAllInterfaces
  {
    private List<MyInterface> _allInterfacesT; //first interface in the chain
    public void AddInterface<T>(MyInterface<T> ifToAdd)
    {
      _allInterfacesT.Add(ifToAdd); // <- this is what I'd like to do
    }
  }

我经常使用这种模式。因为我不知道你的场景的所有细节,所以它可能不适合你。

但它可能会帮助其他人搜索 google。

Is it possible to declare a generic collection to hold only objects implementing a generic interface instantiated with any T?

简答:没有。

更长的答案:不,因为那没有用。

让我们考虑一个简单的通用接口:

interface I<T> { T Get(); }

还有一堆实现它的对象:

class Lion : I<Lion> 
{
  public Lion Get() => this;
}
class TaxPolicyFactory : I<TaxPolicy>
{
  public TaxPolicy Get() => new TaxPolicy();
}
class Door: I<Doorknob>
{
  public Doorknob Get() => this.doorknob;
  ...
}

好的,现在假设你有一个你想要的List<I<ANYTHING>>

var list = new List<I<???>> { new TaxPolicyFactory(), new Lion(), new Door() };

您有一个清单,里面有一个税收政策工厂、一头狮子和一扇门。这些类型彼此之间没有任何共同点;您无法对这些对象中的每一个执行任何操作。即使您可以对它们中的每一个都调用 Get,那么您也会得到一个包含税收政策、狮子和门把手的序列,您打算用它做什么?

没什么,就是这样。约束 "implements interface I<T> for any T" 在 C# 中根本不是有用的约束,因此无法表达它。

听起来您遇到了 "XY" 问题。这是一个你心中有一个糟糕的解决方案的问题,现在你正在问关于你的糟糕解决方案的问题。 向我们询问您遇到的真正问题,而不是关于您的解决方案的坏主意。真正的问题是什么?


更新:随着问题中的新信息,现在更加清楚了。您想要的功能称为 generic interface covariance,这是我最喜欢的 C# 4 功能。

如果您将接口定义更新为

interface ISyncInterface<out T> { ... }

然后您可以在需要 ISyncInterface<Object> 的上下文中使用 ISyncInterface<String>。例如,您可以将 ISyncInterface<Giraffe> 放入 List<ISyncInterface<Animal>> 或其他任何内容。

但是您需要确保您的接口定义仅在协变有效位置中使用T。您的接口 如所述 是有效的,但是如果您想要向您的接口添加方法 void M(T t);,它将不再是协变有效的。 "out" 是一个助记符,告诉你 T 只能用作方法的 output。由于 IEnumerable<T> 也是协变有效的,所以很好; IEnumerable<T>.

T 没有 输入

此外,变体仅适用于通用接口和委托,并且变体类型必须是引用类型。您不能将 ISyncInterface<int> 放入 List<ISyncInterface<Object>>,因为 int 不是引用类型。

SO上有很多关于协变和逆变的帖子;您还应该阅读 Microsoft 文档。这可能是一个令人困惑的功能。如果您对我们如何设计和实现该功能的历史细节感兴趣,请参阅我的博客。