Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interface casting

Tags:

c#

interface

I have a few classes i can't change. They have one property Prop3 in common:

public class c1
{
    public  string Prop1 { get; set; }
    public  string Prop2 { get; set; }
    public  string Prop3 { get; set; }
}

public class c2
{
    public  string Prop2 { get; set; }
    public  string Prop3 { get; set; }
}

public class c3
{
    public  string Prop5 { get; set; }
    public  string Prop3 { get; set; }
}

Now I want to acces this property without knowing the type. I thought of using a Interface:

public interface ic
{
    string Prop3 { get; set; }
}

But this code throws a invalid cast exception:

c1 c1o = new c1() { Prop3 = "test" };
string res = ((ic)c1o).Prop3;
like image 387
Impostor Avatar asked Aug 19 '16 11:08

Impostor


2 Answers

C# doesn't support compile-time duck-typing, so if you can't change your types, no luck on that.

You can access your property using dynamic, which allows runtime duck-typing (no compile time checking though, and you lose intellisense if you use Visual Studio):

c1 c1o = new c1() { Prop3 = "test" };
string res = ((dynamic)c1o).Prop3;

Or via reflection:

c1 c1o = new c1() { Prop3 = "test" };
string res = (string)c1o.GetType().GetProperty("Prop3").GetValue(c1o);

Since there's no compile-time checking, you'll need to handle exceptions in case you pass an instance with no Prop3.

Or if the types are not sealed, you can try implementing your own derived types where you can specify an interface:

public interface ic
{
   string Prop3 { get; set; }
}

public class c1d : c1, ic {}
public class c2d : c2, ic {}
public class c3d : c3, ic {}

This would require you control the creation of the instances though, instances will need to be of type c1d, c2d, c3d, won't work if you get objects of type c1, c2 or c3

You can do explicit type conversions as @David pointed out (which is a clever trick), but that means you'll have two instances of your object. For a very simple case like the one presented in the question, it might do... if you need anything more advanced, that might be quite tricky

like image 98
Jcl Avatar answered Sep 30 '22 12:09

Jcl


Use an adapter-like construction to encapsulate the conversion logic. Of course the downside of this is that you have to modify the class when c4 pops up.

public class Adapter {
    public Adapter(object c) {
        if (!(c is c1 || c is c2 || c is c3))
            throw new NotSupportedException();
        _c = c;
    }

    private readonly object _c;

    public string Prop3 {
        get {
            if (_c is c1) return ((c1)_c).Prop3;
            if (_c is c2) return ((c2)_c).Prop3;
            if (_c is c3) return ((c3)_c).Prop3;
            throw new NotSupportedException();
        }
    }
}

Usage:

var c1o = new c1() { Prop3 = "test" };
var adapter1 = new Adapter(c1);
var res1 = adapter1.Prop3;

var c2o = new c2() { Prop3 = "test" };
var adapter2 = new Adapter(c2);
var res2 = adapter2.Prop3;
like image 35
Maarten Avatar answered Sep 30 '22 12:09

Maarten