'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 文档。这可能是一个令人困惑的功能。如果您对我们如何设计和实现该功能的历史细节感兴趣,请参阅我的博客。
是否可以声明一个通用集合以仅保存实现具有任何通用接口的对象<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 文档。这可能是一个令人困惑的功能。如果您对我们如何设计和实现该功能的历史细节感兴趣,请参阅我的博客。