在C#中使用泛型的相互依赖的接口

时间:2011-12-19 21:07:08

标签: generics interface c#-2.0

如果它很混乱,我道歉。

我有两个主要接口。首先是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&lt; TReading&gt;必须可兑换成
ISENSOR&LT; ISensorReading&LT; ISENSOR&LT;踩踏&GT;&GT;&GT;为了在通用类型或方法中使用它作为参数TSensor ISensorReading&lt; TSensor&gt;

类型ISensorReading&lt; TSensor&gt;必须可转换为ISensorReading&lt; ISensor&lt; ISensorReading&lt; TSensor&gt;&gt;&gt;为了在通用类型或方法中使用它作为参数TReading ISensor&lt; TReading&gt;

我担心这是由于在编译时无法解决的循环引用;但是我想确保像TelemetricSensor这样的派生类型的一致性:ISensor<TelemetricReading>和 TelemetricReading:ISensorReading<TelemetricSensor>

我应该使用哪些其他方法来进行简单的投射和类型安全?

我正在使用.NET 2.0和VS2005

3 个答案:

答案 0 :(得分:1)

你是对的 - 你不能用这种方式使用泛型类型约束来定义它们,因为它会导致无限的嵌套。

你可以做的是拥有一个或另一个的具体实现,例如:

public interface ITelemetricSensorReading : ISensorReading

然后约束传感器以强制执行:

public interface ITelemetricSensor<TReading> : ISensor<TReading> where TReading : ITelemetricSensorReading

答案 1 :(得分:1)

说,你定义了两种类型:

    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(这是我强烈推荐的),或者您可以依靠单元测试来确保所有类型保持一致。

答案 2 :(得分:0)

感谢大家的回答。

最后,这是我为问题选择的解决方案。希望有用。如果你有意见或知道如何在不改变框架的情况下取得成功,请告诉我。

首先,我创建了一个具有最低要求的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; }
}

定义了theese接口并遵循相同的结构,我能够做出第一个实现:一个TelemetricSensor类及其相应的ITelemetricReading

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(...)重载,这样每个都可以正确地提供数据并满足所有接口实现。