4

I have the following generic method for serialising an input object of one type as a super-type as follows:

public string SerialiseAs<TResult, TInput>(TInput input) where TInput : TResult
{
    DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(TResult));
    MemoryStream stream = new MemoryStream();
    ser.WriteObject(stream, input);
    stream.Position = 0;
    StreamReader reader = new StreamReader(stream);
    return reader.ReadToEnd();
}

I have to call this method specifying both generic types as follows:

MySubType x = new MySubType();
string json = SerialiseAs<MySuperType, MySubType>(x);

My question is, why can't TInput be inferred in this situation? Is it because TResult isn't actually used as the return type? The following code is cleaner but won't compile because of the missing input type:

MySubType x = new MySubType();
string json = SerialiseAs<MySuperType>(x);
1
  • C++ much? If only C# had (member) typedefs. I'd like that. But no, it doesn't work that way. Commented May 23, 2012 at 12:10

2 Answers 2

8

My question is, why can't TInput be inferred in this situation?

It can - it's TResult which can't be inferred, and there's no way of specifying "partial" inference.

What you can sometimes do is separate the type parameters into ones for a generic type and ones for a generic method, so you'd end up with:

// Explicitly state TResult, and infer TInput
Serializer<MySuperType>.Serialize(x);
Sign up to request clarification or add additional context in comments.

3 Comments

That makes much more sense. I like the idea of having the output type specified at the class-level but the input type inferred though. Then I can still have the inheritance check on the generic Serialise method. I could also try using a fluent syntax like Serialise(x).As<MySuperType>(), but that's probably a bit complex for what I'm actually using this for.
@AdamRodger: It would be tricky that way round, as when you specify TResult second, you can't impose the TInput : TResult constraint. If you didn't mind losing the compile-time constraints, that wouldn't be a problem of course.
Marked as answer as the actual reason is that you can't partially-specify generic types.
7

Why not just write it like this:

public string SerialiseAs<TResult>(TResult input)
{
    DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(TResult));
    MemoryStream stream = new MemoryStream();
    ser.WriteObject(stream, input);
    stream.Position = 0;
    StreamReader reader = new StreamReader(stream);
    return reader.ReadToEnd();
}

Since TInput derives from TResult, you really don't need to specify at all.

5 Comments

This doesn't work with DataContractJsonSerialiser, hence why I changed the method to take both types. When you pass your instance as MySuperClass then any extra properties in MySubType are omitted from the serialised result. You also don't get the __type attribute in your output, which I need. You have to pass your instance as the sub-type but tell it to serialise as the super-type.
@AdamRodger - That's odd. I don't see any way how the DataContractJsonSerializer could determine the type of the variable you pass it. The WriteObject() method takes a simple object as its parameter. Are you sure about this?
Yep, absolutely certain. I also have a Serialise method that doesn't do the type-shuffling as the above method and with the same input they both produce different output. The __type attribute is missing.
@AdamRodger - Works like a charm for me: http://pastebin.com/4gTfrvuy Are you perhaps doing some magic with Known Types or something? I'm not much familiar with WCF, but it required me to put this odd KnownType attribute on class A before it would work. It also said something about a DataContractResolver in the exception message.
Yeah MySuperType is an abstract class with a number of implementing sub-classes (specified via KnownType). You need the __type attribute added automatically so that you can deserialise at the client. I didn't think that particular detail was important as I had a working method, just interested as to why I couldn't infer the input type. This is only a little helper method for use in my unit tests anyway so I can check required attributes are there and non-required default attributes are omitted etc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.