Свойство переопределения с другим совместимым Типом

Мне нужен базовый класс со свойством, где я могу получить классы с тем же свойством, но различными (совместимыми) типами. Базовый класс может быть абстрактным.

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 = new List<Base>();
        //...
    }
}

Я попробовал его Дженериками, но это дает мне проблему при использовании класса, потому что я хочу сохранить по-другому введенные базовые классы в Списке.

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

public class UseIt
{
    public void use()
    {
        List<BaseG> l = new List<BaseG>(); // requires type argument
        //...
    }
}
18
задан djv 11 May 2016 в 14:51
поделиться

2 ответа

Вот альтернативный подход к предлагаемому решению:

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

По сути, я добавил общий класс посередине, который хранит ваше правильно типизированное свойство. это будет работать при условии, что вам никогда не понадобится доступ к Prop из кода, который выполняет итерацию элементов List . (Вы всегда можете добавить абстрактный метод к Base под названием GetProp , который преобразует универсальный объект в объект, если это необходимо.)

Пример использования:

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 : Добавлен метод GetProp (), чтобы проиллюстрировать, как к свойству можно получить прямой доступ из базового класса.

26
ответ дан 30 November 2019 в 07:38
поделиться

Вы не можете переопределить тип свойства. Взгляните на следующий код:

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

Это полностью допустимый код. Но что произойдет, если вы попытаетесь это сделать?

b.prop = 5;

Целое число можно преобразовать в объект , потому что все происходит от объекта . Но поскольку b на самом деле является экземпляром StrBase , ему придется каким-то образом преобразовать целое число в строку, чего не может. Вот почему вам не разрешено переопределять тип.

Тот же принцип применяется к универсальным типам:

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;

Это потому, что универсальные типы в C # 2.0 инвариантны. C # 4.0 допускает такой тип преобразований, называемый ковариацией и контравариантностью.

Решения

Вы можете преобразовать объект обратно в строку, когда вам это нужно. Вы можете добавить проверку типа в подкласс:

public class StrBase : Base
{
  private string propValue;

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

Вы также можете предоставить типобезопасное свойство в подклассе:

public class StrBase : Base
{
  public string strProp {
    get
    {
      return (string)this.prop;
    }
    set
    {
      this.prop = value;
    }
  }
}
7
ответ дан 30 November 2019 в 07:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: