C# inheritance of fields
This is probably a silly question, but here it goes.Imagine you have the following classes:
public class C
{
}
public class D : C
{
//A subclass of C
}
public class A
{
C argument;
}
Now, I want to have a class开发者_如何学Python B, that inherits from A.This class would obviously inherit the "argument" field, but I wish to force the "argument" field in B to be of type D, rather than C.Since D inherits from C this shouldn't create any problems. So, how would achieve this in c# ?
As Jared points out, this is not possible. However, it is interesting to consider why not.
Suppose the field is private. Then it cannot be accessed in derived class B. (Assuming B is not a nested type of A.)
Suppose the field is not private. Doesn't matter whether it is public, internal, protected or protected internal. Suppose its public and this were legal. Now you have the following situation.
class B : A
{
override public D argument; // "overrides" A.argument
}
class E : C { }
class F
{
public static void M(A a)
{ a.argument = new E(); }
}
...
F.M(new B());
And hey, you just crashed the runtime. You just wrote an object of type E into a field that can only store type D. (We could make similar crashing scenarios for other configurations, like protected fields, and so on.)
Now, you might say, well, instead let me make a read-only property. That I can virtualize:
class A
{
public virtual C Argument { get; }
}
class B : A
{
public override D Argument { ... }
}
Now the problems go away. The public interface isn't writable, so there's no problem with someone writing something in there that is not supported. And the property is virtual, so there's a mechanism for overriding it.
That feature is called "return type covariance", and it is not supported in C# either. See Why the concept of "Covariance" and "Contravariance" are applicable while implementing the methods of an interface?.
Please explain what you are really trying to do, not how you're trying to do it. There is probably a better way.
You could achieve something like so, where A is a generic type with its type parameter constrained to subclasses of C:-
public class C
{
}
public class D : C
{
}
public class A<T> where T:C
{
public T Argument { get; set; }
}
public class B : A<D>
{
}
Bear in mind that you shouldn't expose public (or protected) fields, which is why I've changed them to properties.
This is not supported in C#. You cannot change the type of an existing field, or property or method for that matter, by deriving from a given type.
To make it typesafe, you'll have to do something with generics. Otherwise just do a cast.
But in general, having non-private fields is not a good solution because it breaks the information hiding principe. You should therefore avoid this if possible and use properties and/or methods instead.
Unless I'm completely missing the boat, I think you're asking for something like this
class A
{
public virtual C Argument { get; set; }
}
class B : A
{
D argument = null;
public override C Argument
{
get
{
return argument;
}
set
{
if (value is D)
{
argument = (D)value;
}
else
{
throw new Exception();
}
}
}
}
class C
{
}
class D : C
{
}
...
static void Main()
{
B b = new B();
D arg = new D();
b.Argument = arg;
}
this is a bit of a straightforward question. It is generally bad practise to hide a base class argument with one of the same name in a derived class.
public class B : A { D argument; }
you could also do this ...
public class B : A { public B() { argument = new D(); } }
The argument property/method in B can return a D since D inherits from C, but, as JaredPar wrote, it has to be typed as C.
What you're looking for is a language feature called "return type covariance", which C# doesn't support.
You have another option here: you can "shadow" the field instead of inheriting it. However, this is very dangerous and almost universally a bad idea, because anything assigned to an object as B.argument will not be available if it is upcast and referenced as A.argument.
If you're on C# 4.0, you can get some aspects of what you're attempting by using a generic covariant interface:
public class C
{
public int foo;
}
public class D : C
{
}
public class A : ITest<C>
{
public C foo { get; private set; }
}
public class B : ITest<D>
{
public D foo { get; private set; }
}
public interface ITest<out T> where T : C
{
T foo { get; }
}
static class Program
{
public static void Covariance(ITest<C> test)
{
}
static void Main()
{
A myCVar = new A();
B myDVar = new B();
Covariance(myDVar);
}
}
Note that the Covariance
function will accept a B
(which is an ITest<D>
, not an ITest<C>
), because the ITest
interface has marked the T
parameter with the keyword out
. This tells the compiler that this type parameter will only be used in output operations, so it is safe to substitute any derived class of T
when using this interface.
精彩评论