代码之家  ›  专栏  ›  技术社区  ›  ilivewithian

是否可以基于属性而不是类型使用通用约束?[复制品]

  •  3
  • ilivewithian  · 技术社区  · 15 年前

    我正在尝试编写一个类,它将负责持久化应用程序选项。因为选项需要持久化,所以发送的值必须是可序列化的。

    最初我以为我能写一个签名如下的方法:

    Public Sub SaveOption(Of T As ISerializable)(ByVal id As String, ByVal value As T)
    

    或者如果你喜欢C:

    public void SaveOption<T>(string id, T value) where T : ISerializable  
    

    原则上这是可以的,但是那些有 <Serializable> 属性?最值得注意的例子是System.String,它不实现 ISerializable 但很明显这是一种我应该能够保存的类型。

    那么,有没有一种方法可以根据它们的属性来限制哪些类型在compiletime被允许进入一个方法?

    4 回复  |  直到 14 年前
        1
  •  2
  •   Marc Gravell    15 年前

    您可以对其他类型进行重载-以字符串为例:

    public void SaveOption(string id, string value)
    

    但是,可序列化性是…很棘手;我希望您必须在运行时检查这个。

        2
  •  2
  •   Emperor XLII    14 年前

    属性约束不会实现太多,因为属性通常在编译时不提供任何保证,所以它们是运行时的信息,而不是编译器的信息。

    在序列化的情况下, [Serializable] 属性只是一个指示器,您可以 尝试 运行时序列化,而 ISerializable 保证您可以序列化它,因为您可以 GetObjectData 这是班级的问题,要确保这件事是正确的。

    例如,我可以

    [Serializable]
    class GoodClass
    {
        public object Property { get; set; }
    }
    
    class BadClass
    {
    }
    

    但是 GoodClass 真的不比 BadClass 因为我能做到

    MemoryStream ms = new MemoryStream();
    BinaryFormatter f = new BinaryFormatter();
    
    GoodClass obj = new GoodClass();
    f.Serialize(ms, obj); // OK
    
    obj.Property = new BadClass();
    f.Serialize(ms, obj); // BOOM
    

    编辑: 属性也不会被继承,因此编译器无法确保在运行时传递的对象仍具有所需的属性:

    class NotSoGoodClass : GoodClass // No [Serializable] attribute
    {
    }
    
    ...
    
    SaveOption<GoodClass>( "id", new NotSoGoodClass() ) // oops
    
        3
  •  1
  •   user151323    15 年前

    不。:(

    啊,不,实际上在C 4.0中,他们引入了代码合同。这应该在这里起作用。

    此链接的示例: CLR 4.0: Code Contracts

    public void BuyMoreStuff(Item[] cart, ref Decimal totalCost, Item i)
    {       
        CodeContract.Requires(totalCost >=0);
        CodeContract.Requires(cart != null);
        CodeContract.Requires(CodeContract.ForAll(cart, s => s != i));
    
        CodeContract.Ensures(CodeContract.Exists(cart, s => s == i);
        CodeContract.Ensures(totalCost >= CodeContract.OldValue(totalCost));
        CodeContract.EnsuresOnThrow<IOException>(totalCost == CodeContract.OldValue(totalCost));
    
        // Do some stuff
        …
    }
    
        4
  •  0
  •   Marcel Valdez Orozco    14 年前

    可以检查这一点,但您是对的,它必须在运行时完成,但要比抛出异常更正式。

    public static byte[] SerializeObject<T>(this T obj)
    {
        Contract.Requires(typeof(T).IsSerializable);
        ...
    }