Reputation: 14173
I'm having a hard time to understand why it's returning the base value and not the actual value. I need to be sure there is a Prop
, but I want to use the actual value and not the base value.
public interface IA
{
string Value { get; set; }
}
public class A : IA
{
public String Value { get; set; }
}
public class B
{
public IA Prop { get; set; }
}
public class C : B
{
public new A Prop { get; set; }
public C()
{
Prop = new A();
Prop.Value = "test C";
}
}
public static class D
{
public static string GetDynamicProp(dynamic item)
{
return item.Prop.Value;
}
public static string GetProp<T>(T item) where T : B
{
return item.Prop.Value;
}
}
And then using it:
var test = new C();
Console.WriteLine(test.Prop.Value); // test C
Console.WriteLine(C.GetDynamicProp(test)); // test C
Console.WriteLine(D.GetProp(test)); // null reference exception because it is using the `IA Prop` from `B`
When using a dynamic type it does work, but then I can't use intellisense for example. So I would like to make sure the object send to D.GetProp
implements a property of IA
, but still use the actual value.
I want the Prop
to always be of a type that implements IA
, but it can be an inherited type.
Upvotes: 0
Views: 65
Reputation: 1289
According to your comment you know the actual types at compile time, so in order to be able to use IntelliSense (i.e. having a compile time knowledge of the actual type) and still be able to use a common interface for all your derived classes you can use combination of an interface and a base class with a generic Cast<T>
method like this:
using System;
using System.Text.RegularExpressions;
public interface IA
{
string Value { get; set; }
T Cast<T>() where T : class, IA;
}
public abstract class ABase : IA
{
public virtual string Value { get; set; }
public T Cast<T>() where T : class, IA
{
return this as T;
}
}
public class A : ABase
{
// redundant, not really needed
public override String Value { get; set; }
}
public class A2 : ABase
{
// redundant, not really needed
public override String Value { get; set; }
public String Value2 { get; set; }
}
public class B
{
public IA Prop { get; set; }
}
public class C : B
{
public C()
{
Prop = new A();
Prop.Value = "test C";
}
}
public class C2 : B
{
public C2()
{
Prop = new A2();
Prop.Value = "test C2";
Prop.Cast<A2>().Value2 = "test C2 Value2";
}
}
public static class D
{
public static string GetDynamicProp(dynamic item)
{
Console.WriteLine(item.GetType().FullName);
Console.WriteLine(item.Prop.GetType().FullName);
return item.Prop.Value;
}
public static string GetProp<T>(T item) where T : B
{
Console.WriteLine(item.GetType().FullName);
Console.WriteLine(item.Prop.GetType().FullName);
return item.Prop.Value;
}
}
public class Program
{
public static void Main()
{
var test = new C();
Console.WriteLine(test.Prop.Value); // test C
Console.WriteLine(D.GetDynamicProp(test)); // test C
Console.WriteLine(D.GetProp(test));
var test2 = new C2();
Console.WriteLine(test2.Prop.Value); // test C2
Console.WriteLine(D.GetDynamicProp(test2)); // test C2
Console.WriteLine(D.GetProp(test2)); // test C2
Console.WriteLine(D.GetProp(test2)); // test C2
Console.WriteLine(test2.Prop.Cast<A2>().Value); // test C2
Console.WriteLine(test2.Prop.Cast<A2>().Value2); // test C2 Value2
}
}
The output would then look like this:
test C
C
A
test C
C
A
test C
test C2
C2
A2
test C2
C2
A2
test C2
C2
A2
test C2
test C2
test C2 Value2
Though a Cast<T>()
method invocation takes about 4 times longer than a direct cast (SomeType) baseType
we found it in our projects neglectable (BenchmarkDotNet gives me a speed of 61ns vs 15ns on my dev machine).
Upvotes: 2
Reputation: 109862
You are hiding B.Prop
by using new
in class C
, rather than overriding it.
I expect that was because you couldn't override it, since it has the same name as the property in the base class, but has a different type. You're not allowed to do that.
One way to fix this is to change the type of the property to be the same as that in the base class, and make it virtual. Then you can override
it, like so:
public interface IA
{
string Value { get; set; }
}
public class A : IA
{
public String Value { get; set; }
}
public class B
{
public virtual IA Prop { get; set; }
}
public class C : B
{
public override IA Prop { get; set; }
public C()
{
Prop = new A();
Prop.Value = "test C";
}
}
The code will then print "test c" as you expected.
There's a good reason that you're not allowed to override a property with a different type - it's because you can set the property via a base class, and therefore you could violate the type system if you were allowed to do so.
An example will make it clear. Suppose you have the following:
public interface IMammal
{
}
public class Dog : IMammal
{
public void Bark()
{
Console.WriteLine("Woof");
}
}
public class Cat : IMammal
{
public void Meow()
{
Console.WriteLine("meow");
}
}
public class Pet
{
public virtual IMammal Animal { get; set; }
}
Now suppose further that you are allowed to override Pet.Animal
to be of a type derived from IMammal
:
public class DoggyPet : Pet
{
public override Dog Animal { get; set; } // NOT ALLOWED!
}
If that was allowed, then code like this would compile:
DoggyPet doggy = new DoggyPet();
doggy.Animal = new Dog(); // OK so far.
doggy.Bark(); // No problems.
Pet pet = doggy; // Assign to base class.
pet.Animal = new Cat(); // Yikes! just changed doggy.Animal to a cat!
doggy.Bark(); // What happens now! Ooops.
NOTE: Of course, if you do NOT have a setter for the property then this issue would not arise, so in theory this should be possible. This feature is called "return type covariance" - but C# does NOT support that.
See this thread for more information about "return type covariance" and a potential workaround.
Upvotes: 3