【问题标题】:Classes with the same interface but different types for a property具有相同接口但属性类型不同的类
【发布时间】:2013-03-28 00:52:20
【问题描述】:

我想要这样的设计:

public interface IDifferentTypes
{
}

public class IntegerType : IDifferentTypes
{
    public int value { get; set; }
}

public class StringType : IDifferentTypes
{
    public string value { get; set; }
}

public class DateTimeType : IDifferentTypes
{
    public DateTime value { get; set; }
}

但在接口中定义了属性“值”。

所以我可以这样称呼:

IDifferentTypes someInt = GetSomeInt(); // GetSomeInt() returns a IntegerType object
Assert.AreEqual(5, someInt.value);

IDifferentTypes someString = GetSomeString(); // GetSomeString() returns a StringType object
Assert.AreEqual("ok", someString.value);

问题是每个实现的值类型都不同,最好的处理方法是什么?

【问题讨论】:

    标签: c# .net types interface


    【解决方案1】:

    您可以定义一个通用接口(但它必须是一个属性,或者更严格地说,它不能是一个字段):

    public interface IHasValue<T> {
      T Value { get; }
    }
    

    T 是类型,占位符,如果你愿意的话,你可以这样做:

    public class HasStringValue : IHasValue<string> {
      public string Value { get; private set; }
    }
    

    【讨论】:

    • 接口不能指定字段;它需要是一个属性
    • 如果你只有一个getter,你可以使接口协变,这将允许你有一个List&lt;IHasValue&lt;object&gt;&gt;,其中包含一堆派生类型。如果您需要一个 setter,那么类型是不变的,并且没有合乎逻辑的方式让您拥有不同类型的 IHasValue 对象的集合。这不是(只是)C# 的限制,而是在保持类型安全的同时,您永远无法做任何有意义的事情。
    • @GrantThomas 除了将第一行代码更改为:public interface IHasValue&lt;out T&gt; 之外,您根本不需要进行任何更改。而已。它已经只在接口中定义了一个getter,而实现定义了一个setter这一事实并没有造成任何问题。
    • @ibiza intInt32 的别名。 Int32 not 从对​​象派生。它可以隐式转换为对象,但这是不同的。您需要创建自己的方法/包装器,才能将 IHasValue&lt;T&gt; where T : struct 转换为 IHasValue&lt;object&gt;
    • @LukeH 不,它没有。正如我所说,它可以被隐式转换object,但这种转换涉及装箱操作。值类型必须被装箱才能成为object。从object 派生它意味着不需要转换操作,它可以被视为object 而没有采取任何实际操作。 C# 差异仅限于引用类型的原因特别是因为没有值类型派生自 object 或任何与此相关的东西。如果它们来自任何东西,那么差异可能对它们有用。
    【解决方案2】:

    如果可以,请使用泛型:

    var someInt = GetSomeInt();
    Assert.AreEqual(5, someInt.Value);
    
    var someString = GetSomeString();
    Assert.AreEqual("ok", someString.Value);
    
    // ...
    
    public interface IDifferentTypes<T>
    {
        T Value { get; set; }
    }
    
    public class IntegerType : IDifferentTypes<int>
    {
        public int Value { get; set; }
    }
    
    public class StringType : IDifferentTypes<string>
    {
        public string Value { get; set; }
    }
    
    public class DateTimeType : IDifferentTypes<DateTime>
    {
        public DateTime Value { get; set; }
    }
    

    【讨论】:

      【解决方案3】:
      interface IDifferentTypes
      {
          Object Value { get; set; }
      }
      
      class StringType : IDifferentTypes
      {
          string _value;
      
          public Object Value
          {
              get
              {
                  return _value;
              }
              set
              {
                  _value = value as string;
              }
          }
      }
      

      但这意味着每次您使用StringType.Value 时,您都需要重铸它。您可能还想公开特定类型的公共访问器。您可能还想添加一些防止分配错误类型的保护措施:

      class StringType : IDifferentTypes
      {
          public String StringProperty { get; set; }
      
          public Object Value
          {
              get
              {
                  // works with any type that can auto cast to `Object`
                  return StringProperty;
              }
              set
              {
                  // Optional
                  if( typeof(string) != value.GetType() )
                  {
                      throw new MyException();
                  }
      
                  // works for any nullable type
                  StringProperty = value as string;
      
                  // OR
      
                  // throws an exception if conversion fails
                  StringProperty = (string)value;
              }
          }
      }
      

      【讨论】:

      • 我不喜欢您在 Value setter 中使用 as 运算符。它隐藏了开发人员不小心为属性设置错误类型的情况。
      • 我不是整个设计模式的粉丝,设置错误的类型或在需要访问它时必须重新转换类型会出现各种问题。这完全取决于您如何使用它,是否最好分配 null 或通过使用 (string)value 来获取异常。
      • 同意。就可维护性而言,如果如何使用一个类并不能从其定义或签名中立即看出,那么它很可能会被滥用,因此代价高昂。
      猜你喜欢
      • 1970-01-01
      • 2021-11-08
      • 1970-01-01
      • 2022-01-27
      • 1970-01-01
      • 1970-01-01
      • 2021-07-24
      • 1970-01-01
      相关资源
      最近更新 更多