Я пытаюсь переопределить свойство в базовом классе с другим, но производным типом с тем же именем. Я думаю, что это возможно с помощью ковариации или обобщений, но я не уверен, как это сделать?
Следующий код получает ошибку:
Ошибка 1 «Sun.Cache»: тип должен быть 'OuterSpace.Cache' для соответствия переопределен member 'OuterSpace.Cache'
public class OuterSpace {
public virtual OuterSpaceData Data {get; set;}
public virtual OuterSpaceAnalysis Analysis {get; set;}
public virtual OuterSpaceCache Cache {get; set;}
public class OuterSpaceData {
//Lots of basic Data Extraction routines eg
public virtual GetData();
}
public class OuterSpaceAnalysis {
//Lots of Generic Analysis on Data routines eg
public virtual GetMean();
}
public class OuterSpaceCache {
//Lots of Caches of Past Analysis Results:
public Dictionary> ResultCache;
}
}
public class Sun : OuterSpace {
public override SunData Data {get; set;}
public override SunAnalysis Analysis {get; set;}
public override SunCache Cache {get; set;}
public SunData : OuterSpaceData {
//Routines to specific get data from the sun eg
public override GetData();
}
public SunAnalysis : OuterSpaceAnalysis {
//Routines specific to analyse the sun: eg
public double ReadTemperature();
}
public SunCache : OuterSpaceCache {
//Any data cache's specific to Sun's Analysis
public Dictionary> TempCache;
}
}
public class Moon : OuterSpace {} etc.
Для конечного результата, когда я обращаюсь к объекту "Данные" Sun, я не хочу, чтобы было два объекта данных (Inherited & Base Class), но когда я пытаюсь переопределить свойство, оно требует переменные Sun должны быть того же типа, что и базовый класс. Например:
Sun EarthSun = new Sun()
EarthSun.Analyse() //The OuterSpace Analysis Saves results to Sun Cache:
//Now try use the result:
EarthSun.Cache[0]...
Очень похоже на это, но с производным типом вместо string-array: Переопределения переменных-членов C #, используемые методом базового класса
И этот ответ не имел для меня особого смысла: Как переопределить член базового класса после наследования в C ++
Или, возможно, это означает, что это просто невозможно? Можно ли переопределить с производными типами?
Помощь! :) Есть ли обходной путь?
Это невозможно, как вы этого хотите, в точности.
Вы можете создать «новое» свойство с тем же именем:
public new SunData Data
{
get { return (SunData) base.Data; }
set { base.Data = value; }
}
Это не совсем то же самое, но, вероятно, настолько близко, насколько вы собираетесь получить.
Другой возможный подход:
public SunData SunData { get; set; }
public override OuterSpaceData Data
{
get { return SunData; }
set { SunData = (SunData)value; }
}
Преимущество этого подхода состоит в том, что он гарантирует, что в ваше свойство Data невозможно поместить что-либо, не являющееся SunData. Обратной стороной является то, что ваше свойство Data не является строго типизированным, и вам нужно использовать свойство SunData, если вы хотите, чтобы оно было статически типизировано для SunData.
Есть уродливый способ получить «лучшее из обоих миров» за счет того, что он запутывает код и становится трудным для понимания впоследствии - путем введения промежуточного производного класса, который выполняет «запечатанное переопределение» в свойстве Data базового класса и перенаправляет на другое защищенное свойство с новым именем, а затем позволяет конечному производному классу добавить «новое» свойство данных, которое затем вызывает промежуточное свойство. Это действительно уродливый хакер, и хотя я сделал это, я бы не рекомендовал его.
Если вы ищете путь ковариации, должно сработать что-то вроде этого:
public class OuterSpace<TCacheType> where TCacheType : OuterSpaceCache {
public virtual OuterSpaceData Data {get; set;}
public virtual OuterSpaceAnalysis Analysis {get; set;}
public virtual TCacheType Cache {get; set;}
public class OuterSpaceData {
//Lots of basic Data Extraction routines eg
public virtual GetData();
}
public class OuterSpaceAnalysis {
//Lots of Generic Analysis on Data routines eg
public virtual GetMean();
}
public class OuterSpaceCache {
//Lots of Caches of Past Analysis Results:
public Dictionary<AnalysisType, List<Result>> ResultCache;
}
}
public class Sun : OuterSpace<SunCache> {
public override SunData Data {get; set;}
public override SunAnalysis Analysis {get; set;}
public SunData : OuterSpaceData {
//Routines to specific get data from the sun eg
public override GetData();
}
public SunAnalysis : OuterSpaceAnalysis {
//Routines specific to analyse the sun: eg
public double ReadTemperature();
}
public SunCache : OuterSpaceCache {
//Any data cache's specific to Sun's Analysis
public Dictionary<AnalysisType, List<Result>> TempCache;
}
}
Справедливое предупреждение, это полностью некомпилированный код, и я могу быть совершенно неправ :) Но это попытка понять, что, как я думаю, вам нужно...
О, и я думаю, что это решение только для .Net 4.0, я не думаю, что вы можете сделать это раньше, co-variance была введена там...
Если SunData совместима по назначению с Outerspace Data (наследуется от), то вам не нужно изменять тип OuterSpaceData
Не уверен насчет Generics, но вы можете достичь ограниченного эффекта с помощью простого полиморфизма (предполагая, что SunData наследуется от Data и т.д.)
public class Sun : OuterSpace
{
void SomeInitializationMethod()
{
Data = new SunData();
Analysis = new SunAnalysis(); // etc
}
}
// You could also make casting simpler with a generic method on the base
public T GetData<T>()
{
if (Data is T)
{
return Data as T;
}
return null;
};
Попытайтесь решить эту проблему с помощью дженериков. Следующий класс предоставляет общий базовый класс для OuterSpace
с ограничениями на его параметры, которые обеспечивают соблюдение правил наследования для типов свойств. Для ясности я опустил методы в маленьких типах.
public class OuterSpace<TData, TAnalysis, TCache>
where TData : OuterSpaceData
where TAnalysis : OuterSpaceAnalysis
where TCache : OuterSpaceCache
{
public virtual TData Data { get; set; }
public virtual TAnalysis Analysis { get; set; }
public virtual TCache Cache { get; set; }
}
public class OuterSpaceData { }
public class OuterSpaceAnalysis { }
public class OuterSpaceCache { }
public class Sun : OuterSpace<SunData, SunAnalysis, SunCache>
{
public override SunData Data { get; set; }
public override SunAnalysis Analysis { get; set; }
public override SunCache Cache { get; set; }
}
public class SunData : OuterSpaceData { }
public class SunAnalysis : OuterSpaceAnalysis { }
public class SunCache : OuterSpaceCache { }
Привлекательность этого подхода заключается в том, что он позволяет вам возвращать строго типизированные свойства и , которые обеспечивают соблюдение базовых ограничений наследования для типов этих свойств. Методы полностью переопределяемы; однако имейте в виду, что переопределение вполне может потребоваться, чтобы избежать проблем с приведением типов в реализации базового класса.