简体   繁体   中英

C# how can I access the object properties that not exist in the implemented interface

Please consider the following code. I have an interface named IClass.

public interface IClass
{
    public string MyProperty { get; set; }
}

And I have two classes that implements this interface:

public class ClassA : IClass
{
    public string MyProperty { get; set; }
}

public class ClassB : IClass
{
    public string MyProperty { get; set; }
    public string MyProperty2 { get; set; }
}

And finally I have a method that returns IClass interface:

public static IClass MethodA()
{
    return new ClassB { MyProperty = "A", MyProperty2 = "B" };
}

My problem is I can't access the MyProperty2 from the MethodA

static void Main(string[] args)
{
    Console.WriteLine(JsonSerializer.Serialize(MethodA()));
}

It is only returning {"MyProperty":"A"} How can I actually get the MyProperty2 without using polymorphic model binding? Is there any other ways?

You need to cast

Console.WriteLine(JsonSerializer.Serialize((ClassB) MethodA()))

However, doing so kind of defeats the purpose of using an interface in the first place.

Probably you will have to check and cast it explicitly like

var data = MethodA();
JsonSerializer.Serialize(data is ClassB ? data as ClassB : data as ClassA ) 

Serialization is based off of the type information you supply. In the case of the generic overloads, this is the generic type (the T in Serialize<T> ). You are calling one of the generic overloads and type-inference is picking up IClass . To get around this, use the overload that accepts a Type :

var obj = MethodA();
var json = JsonSerializer.Serialize(obj, obj.GetType(), default);

Or explicitly cast to object so that the generic type is inferred as object :

var json = JsonSerializer.Serialize((object)MethodA(), default);

Or, a semi-combination of the two:

var json = JsonSerializer.Serialize(MethodA(), typeof(object), default);

If you don't want this behavior from the serializer, you would have to cast to the explicit type (assuming you know it and/or can live with a bunch of type checks)

var json = JsonSerializer.Serialize((ClassB) MethodA(), default);

Note that the object solution will only work at the top-level/root object. If you have a property somewhere in your object graph that is declared as IClass it won't serialize how you want. You'd unfortunately have to declare those properties with the object type.

The alternative would be writing a custom converter for your interface type and reflecting over the properties yourself. While overkill for a root object (given the solutions above) it would allow you to handle nested objects of your interface type. A simple converter could delegate to the object's real type.

public class IClassConverter : JsonConverter<IClass> 
{
     public override IClass Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options) => throw new NotImplementedException();

     public override void Write(
        Utf8JsonWriter writer,
        IClass obj,
        JsonSerializerOptions options)
     {
         JsonSerializer.Serialize(writer, obj, obj.GetType(), default); // don't pass options, will result in endless loop 
     } 
} 

You then use it by adding your converter to the Serializer Options:

var opts = new JsonSerializerOptions();
opts.Converters.Add(new IClassConverter());
IClass obj = MethodA();
var json = JsonSerializer.Serialize(obj, opts);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM