是否可以使用基于属性而不是类型的通用约束?

发布于 2024-08-03 00:38:28 字数 495 浏览 3 评论 0原文

我正在尝试编写一个负责持久应用程序选项的类。由于需要保留选项,因此我发送的值必须是可序列化的。

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

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  

原则上这没问题,但是具有 属性的类型又如何呢? ?最值得注意的例子是 System.String,它没有实现 ISerialized,但显然它是我应该能够保存的类型。

那么,有没有一种方法可以根据类型的属性来限制在编译时方法中允许哪些类型?

I'm trying to write a class that will be in charge of persisting application options. Since the options need to be persisted the values that I'm sent must be serialisable.

Initially I thought I've be able to write a method with a signature like this:

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

or if you prefer C#:

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

In principle this would be fine, but what about types that have the <Serializable> attribute? The most notable example of this is System.String, it does not implement ISerializable, but clearly it is a type that I should be able to save.

So, is there a way that I can restrict which types are allowed into a method at compiletime, based on their attributes?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

池木 2024-08-10 00:38:28

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

public void SaveOption(string id, string value)

但是;可串行化是......棘手的;我希望您必须在运行时检查这一点。

You could have overloads for the other types - taking your string example:

public void SaveOption(string id, string value)

However; serializability is... tricky; I expect you're going to have to check this at runtime.

草莓味的萝莉 2024-08-10 00:38:28

属性约束不会有太大作用,因为属性通常在编译时不提供任何保证,它们是运行时而不是编译器的信息。

在序列化情况下,[Serializable] 属性仅指示您可以在运行时尝试序列化,而 ISerialized 则保证您可以序列化它,因为您绝对可以调用 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

EDIT: 属性也不是继承的,所以编译器无法确定在运行时传递的对象是否仍具有您所需的属性:

class NotSoGoodClass : GoodClass // No [Serializable] attribute
{
}

...

SaveOption<GoodClass>( "id", new NotSoGoodClass() ) // oops

An attribute constraint wouldn't achieve much, since an attribute doesn't generally offer any guarantees at compile time, they're information for the runtime rather than the compiler.

In the serialization case, the [Serializable] attribute is only an indicator that you can attempt serialization at runtime, while ISerializable guarantees that you can serialize it, because you can definitely call GetObjectData and it's the class's problem to ensure that that does the right thing.

For example, I can have

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

class BadClass
{
}

but GoodClass is really no better than BadClass, because I can do

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

EDIT: Attributes also aren't inherited, so the compiler can't be sure that the object passed at runtime will still have the attribute you demanded:

class NotSoGoodClass : GoodClass // No [Serializable] attribute
{
}

...

SaveOption<GoodClass>( "id", new NotSoGoodClass() ) // oops
久随 2024-08-10 00:38:28

没有。 :(

啊,不,实际上在 C# 4.0 中他们引入了代码契约。这应该可以在这里工作。

此链接的示例:CLR 4.0:代码合同

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
    …
}

Nope. :(

Ah, no, actually in C# 4.0 they've introduced code contracts. That should work here.

Example from this link: 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
    …
}
数理化全能战士 2024-08-10 00:38:28

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

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

It is possible to check for this, but you're right, it will have to be done at runtime, but more formally than just throwing an exception.

public static byte[] SerializeObject<T>(this T obj)
{
    Contract.Requires(typeof(T).IsSerializable);
    ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文