开发者

Override Property with different compatible Type

I need a base class with a property where I can derive classes with the same property but different (compatible) types. The base Class can be abstract.

public class Base
{
    public virtual object prop { get; set; }
}

public class StrBase : Base
{
    public override string prop { get; set; } // compiler error
}

public class UseIt
{
    public void use()
    {
        List<Base> l = n开发者_JAVA百科ew List<Base>();
        //...
    }
}

I tried it with Generics but that gives me a problem when using the class, because I want to store differently typed base classes in the List.

public class BaseG<T>
{
    public T prop { get; set; }
}

public class UseIt
{
    public void use()
    {
        List<BaseG> l = new List<BaseG>(); // requires type argument
        //...
    }
}


Here's an alternative approach to proposed solution:

public abstract class Base
{
    public abstract void Use();
    public abstract object GetProp();
}

public abstract class GenericBase<T> : Base
{
    public T Prop { get; set; }

    public override object GetProp()
    {
        return Prop;
    }
}

public class StrBase : GenericBase<string>
{
    public override void Use()
    {
        Console.WriteLine("Using string: {0}", Prop);
    }
}

public class IntBase : GenericBase<int>
{
    public override void Use()
    {
        Console.WriteLine("Using int: {0}", Prop);
    }
}

Basically I've added a generic class in the middle that stores your properly-typed property. this will work assuming that you never need to access Prop from the code that iterates the members of the List<Base>. (You could always add an abstract method to Base called GetProp that casts the generic to an object if that's required.)

Sample usage:

class Program
{
    static void Main(string[] args)
    {
        List<Base> l = new List<Base>();

        l.Add(new StrBase {Prop = "foo"});
        l.Add(new IntBase {Prop = 42});

        Console.WriteLine("Using each item");
        foreach (var o in l)
        {
            o.Use();
        }
        Console.WriteLine("Done");
        Console.ReadKey();
    }
}

Edit: Added the GetProp() method to illustrate how the property can be directly accessed from the base class.


You can't override the type of a property. Take a look at the following code:

StrBase s = new StrBase();
Base b = s;

This is completely valid code. But what happens when you try to do this?

b.prop = 5;

The integer can be converted to object, because everything is derived from object. But since b is actually a StrBase instance, it would have to convert the integer to a string somehow, which it can't. So that is why you aren't allowed to override the type.

The same principle applies to generics:

List<BaseG<object>> l = new List<BaseG<object>>();
BaseG<string> s = new BaseG<string>();

// The compiler will not allow this.
l.add(s);

// Here's the same problem, convert integer to string?
BaseG<object> o = l[0];
o.prop = 5;

This is because generic types in C# 2.0 are invariant. C# 4.0 does allow this type of conversions, called covariance and contravariance.

Solutions

An option is to cast the object back to string when you need it. You could add type validation in the subclass:

public class StrBase : Base
{
  private string propValue;

  public override object prop {
    get
    {
      return this.propValue;
    }
    set
    {
      if (value is string)
      {
        this.propValue = (string)value;
      }
    }
  }
}

You could also expose a type-safe property in the subclass:

public class StrBase : Base
{
  public string strProp {
    get
    {
      return (string)this.prop;
    }
    set
    {
      this.prop = value;
    }
  }
}


This is possible since C# 9.0

Beginning with C# 9.0, override methods support covariant return types.

(see Microsoft docs)


public class First
{
    private int someV;

    public virtual object SomeV { get => someV; set => someV = (int)value; }

    public First() { }
}

public class Two : First
{
    private string someV;

    public override object SomeV { get => someV; set => someV = value.ToString(); }

    public Two() { }
}

and use of those:

First firstClass = new First();
firstClass.SomeV = 1;

Two twoClass = new Two();
twoClass.SomeV = "abcd";
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜