Interface casting

I have several classes that I cannot change. They have one Prop3 property:

 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 access this property without knowing the type. I was thinking about using an interface:

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

But this code throws an invalid casting exception:

 c1 c1o = new c1() { Prop3 = "test" }; string res = ((ic)c1o).Prop3; 
+7
c # interface
source share
5 answers

C # does not support duck-typing compilation, so if you cannot change your types, you're out of luck.

You can access your property with dynamic , which allows you to use duck print at runtime (there is no compilation time check, and you lose intellisense if you use Visual Studio):

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

Or through reflection:

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

Since there is no compile time check, you need to handle exceptions if you pass the instance without Prop3 .

Or, if the types are not sealed, you can try to implement your derived types, where you can specify the interface:

 public interface ic { string Prop3 { get; set; } } public class c1d : c1, ic {} public class c2d : c2, ic {} public class c3d : c3, ic {} 

To do this, you need to control the creation of instances, but instances must be of type c1d , c2d , c3d , will not work if you get objects of type c1 , c2 or c3

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

+11
source share

Use an adapter-like construct to encapsulate the conversion logic. Of course, the disadvantage of this is that you have to change the class when c4 appears.

 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(); } } } 

Using:

 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; 
+7
source share

Another way to approach this is to use reflection to get the value of the property with the specified name.

For example, write a simple helper method like this:

 public static T GetProperty<T>(object obj, string name) { return (T) obj.GetType().GetProperty(name).GetValue(obj); } 

Then the following unrelated classes are defined:

 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; } } public class C4 { public string Prop4 { get; set; } public string Prop5 { get; set; } } 

you can access the properties of Prop3 as follows:

 object c1 = new C1 {Prop3 = "C1"}; object c2 = new C2 {Prop3 = "C2"}; object c3 = new C3 {Prop3 = "C3"}; object c4 = new C4(); Console.WriteLine(GetProperty<string>(c1, "Prop3")); //Prints C1 Console.WriteLine(GetProperty<string>(c2, "Prop3")); //Prints C2 Console.WriteLine(GetProperty<string>(c3, "Prop3")); //Prints C3 Console.WriteLine(GetProperty<string>(c4, "Prop3")); // Throws an exception. 
+3
source share

Because this is an invalid listing. c1 does not implement ic interface:

 public class c1 { // ... } 

To make a throw, objects must be legitimately polymorphic:

 public class c1 : ic { // ... } 

But if you really can’t change the class in any way, then the types are not polymorphic and completely unrelated. To go from one to another, you will need to manually convert:

 public class someClass : ic { string Prop3 { get; set; } } //... c1 c1o = new c1() { Prop3 = "test" }; string res = (new someClass { Prop3 = c1o.Prop3 }).Prop3; 

An example, of course, is very contrived. But you got the idea. Two completely unrelated types cannot be dropped from one to the other. You will need to convert them.

You can encapsulate this transformation in various ways. A helper method, factory, even an explicit conversion operator (which allows you to perform the conversion that you originally tried)

 public class someClass : ic { string Prop3 { get; set; } public static explicit operator someClass(c1 c1o) { return new someClass { Prop3 = c1o.Prop3 }; } } // which should allow this... c1 c1o = new c1() { Prop3 = "test" }; string res = ((ic)c1o).Prop3; 
+1
source share

Instead of dealing with reflection / dynamics and pitfalls, your method can easily be divided into 3 overloads:

 public string GetString(c1 item){ return item.Prop3;} public string GetString(c2 item){ return item.Prop3;} public string GetString(c3 item){ return item.Prop3;} 

Downside is a bit of repetitive code, but upstream there are no exceptions and no compile-time type checking.

+1
source share

All Articles