Переопределить свойство с другим совместимым типом
Мне нужен базовый класс с свойством, в котором я могу получить классы с тем же свойством, но с разными (совместимыми) типами. Базовый класс может быть абстрактным.
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>();
//...
}
}
Я попробовал его с помощью Generics, но это создает проблемы при использовании класса, потому что я хочу хранить в List 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
//...
}
}
Ответы
Ответ 1
Здесь альтернативный подход к предлагаемому решению:
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>
. (Вы всегда можете добавить абстрактный метод к 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();
}
}
Изменить: добавлен метод GetProp(), чтобы проиллюстрировать, как свойство можно напрямую получить из базового класса.
Ответ 2
Вы не можете переопределить тип свойства. Взгляните на следующий код:
StrBase s = new StrBase();
Base b = s;
Это вполне допустимый код. Но что происходит, когда вы пытаетесь это сделать?
b.prop = 5;
Целое число можно преобразовать в object
, потому что все получено из object
. Но так как 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 the same problem, convert integer to string?
BaseG<object> o = l[0];
o.prop = 5;
Это связано с тем, что общие типы в С# 2.0 являются инвариантными. С# 4.0 разрешает этот тип преобразований, называемый ковариацией и контравариантностью.
Решения
Опция заключается в том, чтобы вернуть object
обратно в строку, когда вам это нужно. Вы можете добавить проверку типов в подкласс:
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;
}
}
}
Ответ 3
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() { }
}
и их использование:
first firstClass = new first();
firstClass.SomeV = 1;
two twoClass = new two();
twoClass.SomeV = "abcd";
Ответ 4
Я тоже столкнулся с этой проблемой, но нашел очень элегантный способ сделать это без универсальных типов.
Я написал полную статью на эту тему прямо здесь:
http://materiagame.com/2019/09/04/override-property-type-in-a-derived-class
TheUncas21