tags:

views:

208

answers:

8

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 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# ?

+1  A: 

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.

Lucero
The field is going to be "protected" most probably, I just left this out of the example, because it doesn't have much to do with the main problem:)
Emil D
`protected` is just as bad.
Lucero
+2  A: 

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.

JaredPar
A: 

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

yamspog
I thought of that, but that allows whoever is maintaining my code to do argument = new C(); at some point, and I'd rather not let them:)
Emil D
+3  A: 

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.

AdamRalph
A: 

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.

Jamie Ide
+10  A: 

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 http://stackoverflow.com/questions/2502252.

Please explain what you are really trying to do, not how you're trying to do it. There is probably a better way.

Eric Lippert
Does anyone have a thin metal ruler?
Bryan Watts
Am I being an idiot, or do you mean F.M( new B() )But I see your point:)
Emil D
+1  A: 

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;
}
Anthony Pegram
Seems that this would work.Thanks, I hadn't thought of that:)
Emil D
Just know you'll have to cast it back to D when you retrieve it back from the property. *D d = (D)b.Argument;* But like Eric pointed out, be sure that's what you really want to do.
Anthony Pegram
A: 

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.

Dan Story