【问题标题】:Interdependent interfaces using generics in C#在 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 类型必须可转换为
ISensor>> 以便将其用作泛型类型或方法中的参数 TSensor ISensorReading

类型 ISensorReading 必须可转换为
ISensorReading>> 才能将其用作泛型类型或方法 ISensor 中的参数 TReading

我担心这是由于编译时无法解决的循环引用;但是我想确保派生类型如 TelemetricSensor 的一致性:ISensor&lt;TelemetricReading&gt; 和 遥测阅读:ISensorReading&lt;TelemetricSensor&gt;

我应该使用哪些其他方法来实现简单的强制转换和类型安全?

我正在使用 .NET 2.0 和 VS2005

【问题讨论】:

    标签: generics interface c#-2.0


    【解决方案1】:

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

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

    public interface ITelemetricSensorReading : ISensorReading
    

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

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

    【讨论】:

      【解决方案2】:

      说,你定义了你的两种类型:

          class AReading : ISensorReading<ASensor> { }
          class ASensor : ISensor<AReading> { }
      

      现在,声明 ISensorReading&lt;ASensor&gt; 是非法的,因为 ASensor 没有实现 ISensor&lt;ISensorReading&lt;ASensor&gt;&gt;。相反,它实现了ISensor&lt;Areading&gt;,这是不同的。

      请参阅,在 .NET 中,声明 A : B 通常并不暗示声明 I&lt;A&gt; : I&lt;B&gt;。如果你仔细想想,这不一定是真的——取决于I的性质。

      您要查找的特征称为“协方差”和“逆变”。这是 C# 的另一个特性,您可以在其中告诉编译器,对于您的特定接口 I,上述含义实际上确实成立(协方差),或者相反的含义 I&lt;B&gt; : I&lt;A&gt; 成立(逆变)。

      您使用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(...) 进行多次重载,以便每个都提供正确转换的数据并满足所有接口实现。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-02-10
        • 2018-10-20
        • 2019-03-23
        • 1970-01-01
        • 2013-03-26
        • 2013-10-21
        • 1970-01-01
        相关资源
        最近更新 更多