在 C# 中使用泛型的相互依赖的接口
Posted
技术标签:
【中文标题】在 C# 中使用泛型的相互依赖的接口【英文标题】:Interdependent interfaces using generics in C# 【发布时间】:2011-12-19 21:07:08 【问题描述】:如果让您感到困惑,我深表歉意。
我有这两个主要界面。首先是ISensor
接口:
public interface ISensor<TReading>
where TReading : ISensorReading<ISensor<TReading>>
event SensorReadingCompletedEH<ISensor<TReading>, TReading> ReadCompleted;
TReading Read();
还有第二个接口:ISensorReading
:
public interface ISensorReading<TSensor>
where TSensor : ISensor<ISensorReading<TSensor>>
TSensor Sensor get;
会导致如下错误:
ISensor
和
类型 ISensorReading
我担心这是由于编译时无法解决的循环引用;但是我想确保派生类型如 TelemetricSensor 的一致性:ISensor<TelemetricReading>
和
遥测阅读:ISensorReading<TelemetricSensor>
我应该使用哪些其他方法来实现简单的强制转换和类型安全?
我正在使用 .NET 2.0 和 VS2005
【问题讨论】:
【参考方案1】:你是对的——你不能以这种方式使用泛型类型约束来定义它们,因为它会导致无限嵌套。
你可以做的是有一个具体的实现,例如:
public interface ITelemetricSensorReading : ISensorReading
然后约束传感器强制执行:
public interface ITelemetricSensor<TReading> : ISensor<TReading> where TReading : ITelemetricSensorReading
【讨论】:
【参考方案2】:说,你定义了你的两种类型:
class AReading : ISensorReading<ASensor>
class ASensor : ISensor<AReading>
现在,声明 ISensorReading<ASensor>
是非法的,因为 ASensor
没有实现 ISensor<ISensorReading<ASensor>>
。相反,它实现了ISensor<Areading>
,这是不同的。
请参阅,在 .NET 中,声明 A : B
通常并不暗示声明 I<A> : I<B>
。如果你仔细想想,这不一定是真的——取决于I
的性质。
您要查找的特征称为“协方差”和“逆变”。这是 C# 的另一个特性,您可以在其中告诉编译器,对于您的特定接口 I
,上述含义实际上确实成立(协方差),或者相反的含义 I<B> : I<A>
成立(逆变)。
您使用out
关键字实现的第一个:
interface I<out T> ...
第二个 - 使用 in
关键字:
interface I<in T> ...
然而,不幸的是,泛型类型参数中的协变和逆变只在 C# 4.0 中引入,所以你在这里不走运。
您可以升级到 C# 4.0(这是我强烈推荐的),或者您可以依靠您的单元测试来确保所有类型保持一致。
【讨论】:
确实有时协变和逆变会导致头痛。不幸的是,我的应用程序必须尽可能与 MONO 兼容,并且不能选择升级到 C# 4.0。我还注意到 .NET 4.0 不如 .NET 2.0 高效 .net 4.0 效率低于 2.0?究竟如何?【参考方案3】:感谢大家的回答。
最后,这是我为该问题选择的解决方案。希望有用。如果您有 cmets 或知道在不更改框架的情况下改进它的方法,请告诉我。
首先,我创建了一个要求最低的 ISensor 接口。之后我定义了一个新的 ISensor 通用接口如下:
public delegate void SensorErrorEventHandler<TSensor>(TSensor sensor, ISensorError error)
where TSensor : ISensor;
public delegate void SensorReadingCompletedEventHandler<TSensor, TReading>(TSensor sensor, TReading[] read)
where TSensor : ISensor
where TReading : ISensorReading<TSensor>;
public interface ISensor : IDisposable
bool IsOpen get;
bool Started get;
void Connect();
void Disconnect();
void Start();
void Stop();
public interface ISensor<TSensor, TReading> : ISensor
where TSensor : ISensor
where TReading : ISensorReading<TSensor>
TReading[] LastReadings get;
event SensorErrorEventHandler<TSensor> Error;
event SensorReadingCompletedEventHandler<TSensor, TReading> ReadCompleted;
bool Read(out TReading[] readings);
public interface ISensorReading<TSensor> where TSensor : ISensor
TSensor Sensor get;
bool Mistaken get;
定义了这些接口并遵循相同的结构,我能够进行第一个实现:具有对应 ITelemetricReading 的 TelemetricSensor 类
public delegate void TelemetricSensorThresholdExceededEventHandler<TSensor>(TSensor sensor)
where TSensor : ITelemetricSensor;
public interface ITelemetricSensor : ISensor
/* Properties, events and methods */
public interface ITelemetricReading : ISensorReading<ITelemetricSensor>
/* Properties, events and methods */
public abstract class TelemetricSensor<TSensor, TReading> : ITelemetricSensor, ISensor<TSensor, TReading>
where TSensor : ITelemetricSensor
where TReading : ITelemetricReading, ISensorReading<TSensor>
public abstract TReading[] LastReadings get;
public event SensorErrorEventHandler<TSensor> Error;
public event SensorReadingCompletedEventHandler<TSensor, TReading> ReadCompleted;
public abstract bool Read(out TReading[] readings);
TelemetricSensor 抽象类的 TReading 很有趣。它被定义为
TReading : ITelemetricReading, ISensorReading<TSensor>
这可能看起来是多余的,因为 ITelemetricReading 继承自 ISensorReading,但需要编译代码并满足两者的要求
TReading[] LastReadings get;
财产和
bool Read(out TReading[] readings);
方法。最后,为了跳过繁琐的转换(可能还有一些错误),可以对 Read(...) 进行多次重载,以便每个都提供正确转换的数据并满足所有接口实现。
【讨论】:
以上是关于在 C# 中使用泛型的相互依赖的接口的主要内容,如果未能解决你的问题,请参考以下文章