views:

236

answers:

3

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?

+1  A: 

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
    …
}
Developer Art
+2  A: 

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.

Marc Gravell
I could add overloads for a few system types, but there are a large number of other types that I need to support that don't implement the interface. I guess I'll have to check stuff at run time, which is a shame.
ilivewithian
+2  A: 

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, new GoodClass()); // OK

obj.Property = new BadClass();
f.Serialize(ms, new BadClass()); // 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
stevemegson