1

I have a set of classes as follows: a Command, which Executes and stores a Result; a Response, which is created as in order to return the Result in a serialized form (plus extra metadata which I've left out). The Response.Result must be of type object, as it is used for a bunch of different commands, each of which can have a Result of any type at all.

The Command is generic, and I'd like it to accept an interface rather than concrete type, but when I do, the serialized response contains the following type hint:

"__type":"ResultOfanyType:#serialization"

rather than the following, which is generated when the command accepts a concrete type:

"__type":"ResultOfMyObjectDhOQ6IBI:#serialization"

I need the type hint to contain the concrete type rather than ResultOfanyType. Why are interfaces being treated differently in this context? Notice that when the Type is a direct property of the serialized Command, then the concrete type is contained in the type hint

I've tried changing the the Result's Response property typed to Result, but that has no effect.

Here is the code. Simply uncomment/comment the lines in Main where the command is created and known types listed for the alternative version.

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;

namespace serialization
{
    class Program
    {
        static void Main(string[] args)
        {
            Response response = new Response();
            response.ResponseStatus = "ok";
            ConcreteCommand command = new ConcreteCommand();    //switch with line below to test inteface
            //InterfaceCommand command = new InterfaceCommand();
            command.Execute();
            response.Results = command.Results;
            List<Type> knownTypes = new List<Type>
            {
            typeof(Result<MyObject>),                  //switch with Interface lines below to test inteface
            typeof(MyObject)
            //typeof(Result<IMyObject>),
            //typeof(IMyObject)
            };
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(response.GetType(), knownTypes, int.MaxValue, false, null, true);
            Stream stream = new MemoryStream();
            serializer.WriteObject(stream, response);
            stream.Position = 0;
            StreamReader reader = new StreamReader(stream);
            string output = reader.ReadToEnd();
            Console.WriteLine(output);
        }
    }

    public interface IMyObject
    {
        string name { get; set; }
    }

    [DataContract]
    [KnownType(typeof(MyObject))]
    public class MyObject : IMyObject
    {
        [DataMember]
        public string name { get; set; }
    }

    [DataContract]
    public class Result<T>
    {
        [DataMember]
        public string Status { get; set; }

        [DataMember]
        public T Item { get; set; }
    }

    public abstract class BaseCommand<T>
    {
        protected Result<T> results = new Result<T>();

        protected T resultObject;

        public object Results
        {
            get { return this.results; }
        }

        public T ResultObject
        {
            get { return this.resultObject; }
        }

        public abstract void Execute();
    }

    public class InterfaceCommand : BaseCommand<IMyObject>
    {
        public override void Execute()
        {
            IMyObject myobject = new MyObject();
            myobject.name = "my object";
            Result<IMyObject> result = new Result<IMyObject>();
            result.Item = myobject;
            result.Status = "ok";
            this.results= result;
            this.resultObject = myobject;
        }
    }

    public class ConcreteCommand : BaseCommand<MyObject>
    {
        public override void Execute()
        {
            MyObject myobject = new MyObject();
            myobject.name = "my object";
            Result<MyObject> result = new Result<MyObject>();
            result.Item = myobject;
            result.Status = "ok";
            this.results = result;
            this.resultObject = myobject;
        }
    }

    [DataContract]
    public class Response
    {
        [DataMember]
        public string ResponseStatus { get; set; }

        [DataMember]
        public object Results { get; set; }
    }
}
dbc
  • 104,963
  • 20
  • 228
  • 340
darasd
  • 2,899
  • 3
  • 26
  • 39
  • I've done something very similar to this. I'll try and post something a little later on, when I have time. I think it'd help if you tell us what you're trying to achieve. – Mick Mar 17 '14 at 05:24

2 Answers2

0

Let's start with this question and that might explain everything.

I need the type hint to contain the concrete type rather than ResultOfanyType. Why are interfaces being treated differently in this context?

An interface is basically just a contract for what a class implementing it should contain and multiple classes could implement its members. For example.

public interface IPerson
{
    int Id { get; set; }
    string FirstName { get; set; }
    string LastName { get; set; }
}

public class Person : IPerson
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public DateTime DateOfBirth { get; set; }
}

public class Contact : IPerson
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public string Company { get; set; }
    public string PhoneNumber { get; set; }
}

So when you call an IPerson what are you expecting back? A Person or a Contact? Each has an id and the basic components of a name, but each also has unique properties that IPerson doesn't even know exist. This is why when you try to get an interface to resolve to a concrete class, you're not going to get anywhere without some sort of factory class to figure out what you want. So in this case, if I wanted to resolve an IPerson, I'd add the following line of code...

var objectType = iPersonObject.GetType();

In your case, you'd want to try calling GetType() on result.Item. This tells .NET to look at the actual type of the object implementing the interface and return it.

gfish3000
  • 1,557
  • 1
  • 11
  • 22
  • The KnownType attributes can be used to associate an interface with an type for a class. I don't think what he's trying to do is entirely impossible. It's just ill constructed. Will try post something more constructive when I have time. – Mick Mar 17 '14 at 05:22
  • @gfish3000 Whilst what you say makes sense, and, indeed, was what I assumed was happening, it appears not to be entirely the case. I've added a ResultObject property above, to illustrate that when the Interface type is a direct property of the serialized Command, then the type hint does show the concrete type. And yes, I've overcome the issue by using GetType on deserialization, but it's a rather clunky solution. – darasd Mar 17 '14 at 10:12
  • Yeah, I can understand how it might get clunky. If you tell us exactly what you're trying to achieve and why, perhaps we could simplify it to make it a lot less clunky. – gfish3000 Mar 17 '14 at 12:09
0

How about this...

class Program
{
    static void Main(string[] args)
    {
        Response response = new Response();
        response.ResponseStatus = "ok";
        //ConcreteCommand command = new ConcreteCommand();    //switch with line below to test inteface
        InterfaceCommand command = new InterfaceCommand();
        command.Execute();
        response.Results = command.Results;
        List<Type> knownTypes = new List<Type>
        {
            typeof(MyObject),
            typeof(Result<MyObject>)                  //switch with line below to test inteface
            //typeof(Result<IMyObject>)
        };
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(response.GetType(), knownTypes, int.MaxValue, false, null, true);
        Stream stream = new MemoryStream();
        serializer.WriteObject(stream, response);
        stream.Position = 0;
        StreamReader reader = new StreamReader(stream);
        string output = reader.ReadToEnd();
        Console.WriteLine(output);
    }
}

public interface IMyObject
{
    string name { get; set; }
}

[DataContract]
public class MyObject : IMyObject
{
    [DataMember]
    public string name { get; set; }
}

[DataContract]
public class Result<T>
{
    [DataMember]
    public string Status { get; set; }

    [DataMember]
    public T Item { get; set; }
}

public abstract class BaseCommand
{
    protected Result<IMyObject> results = new Result<IMyObject>();

    public Result<IMyObject> Results
    {
        get { return this.results; }
    }

    public abstract void Execute();
}

public class InterfaceCommand : BaseCommand
{
    public override void Execute()
    {
        IMyObject myobject = new MyObject();
        myobject.name = "my object";
        Result<IMyObject> result = new Result<IMyObject>();
        result.Item = myobject;
        result.Status = "ok";
        this.results= result;
    }
}

public class ConcreteCommand : BaseCommand
{
    public override void Execute()
    {
        MyObject myobject = new MyObject();
        myobject.name = "my object";
        Result<IMyObject> result = new Result<IMyObject>();
        result.Item = myobject;
        result.Status = "ok";
        this.results = result;
    }
}

[DataContract]
public class Response
{
    [DataMember]
    public string ResponseStatus { get; set; }

    [DataMember]
    public Result<IMyObject> Results { get; set; }
}

Outputs...

{"__type":"Response:#ConsoleApplication2","ResponseStatus":"ok","Results":{"__ty
pe":"ResultOfanyType:#ConsoleApplication2","Item":{"__type":"MyObject:#ConsoleAp
plication2","name":"my object"},"Status":"ok"}}

If you're trying to make some sort of generic contract, you're going to have to have some sort of common base class/interface. It won't work with object but you can go ala COM and make your own IUnknown interface from which to create as many subclasses as you like, as long as they are included within your known types.

Mick
  • 6,527
  • 4
  • 52
  • 67