C# Наследование, новый модификатор и дженерики
Я с трудом пытаюсь найти правильный подход к этому:
Мои структуры данных:
public abstract class Flow
{
public virtual double Value { get; set; }
public virtual DateTime Time { get; set; }
}
public class InboundFlow : Flow
{
}
public class OutboundFlow : Flow
{
}
Мои бизнес-объекты, содержащие коллекции этих структур данных
public abstract class Fluent
{
public virtual IList<Flow> FlowCollection { get; set; }
public virtual double InitialBaseflow { get; set; }
}
public class Affluent : Fluent
{
public new virtual IList<InboundFlow> FlowCollection { get; set; }
}
public class Effluent : Fluent
{
public new virtual IList<OutboundFlow> FlowCollection { get; set; }
}
Общий метод, который я пытаюсь использовать:
private static void FindInitialBaseflow<T>(ref T fluent) where T : Fluent
{
var linqFluent = fluent;
var flows = linqFluent.FlowCollection.ToList().FindAll(
flow =>
flow.Time >= SOME_DATE &&
flow.Time < SOME_OTHER_DATE);
var initialBaseflow = flows.Average(flow => flow.Value);
fluent.InitialBaseflow = Math.Round(initialBaseflow, 5);
}
Моя проблема заключается в том, что вызов "linqfluent.FlowCollection" в методе linq вызывает базовый класс Fluent's FlowCollection, который является нулевым.
Как я могу заставить использование имущества ребенка вместо этого? Спасибо!
2 ответа
Вы должны сделать коллекцию в течение Fluent
универсальный, так что классы, которые наследуются от него, могут указывать тип:
public class Fluent<T>
where T : Flow
{
public IList<T> FlowCollection { get; set; }
public double InitialBaseflow { get; set; }
}
Если у вас есть, что вам даже не нужны подклассы Flow
Вы можете просто сделать это конкретным.
Ваше использование этого было бы легко изменено, чтобы соответствовать этой модели:
private static void FindInitialBaseflow<T>(Fluent<T> fluent)
where T : Flow
{
var linqFluent = fluent;
var flows = linqFluent.FlowCollection.Where(
flow =>
flow.Time >= SOME_DATE &&
flow.Time < SOME_OTHER_DATE);
var initialBaseflow = flows.Average(flow => flow.Value);
fluent.InitialBaseflow = Math.Round(initialBaseflow, 5);
}
Также обратите внимание, что, поскольку вы не устанавливаете fluent
в этом методе нет необходимости передавать его по ссылке. Это уже класс, поэтому он сам по себе является ссылкой; мутации указанного объекта будут наблюдаться вызывающим абонентом.
Дженерики - неправильный инструмент. Вы должны использовать полиморфизм, чтобы гарантировать, что правильная реализация вызывается в зависимости от типа.
Например:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApp
{
public abstract class Flow
{
public virtual double Value { get { return new Random().Next() ; } }//these values are just for demonstration purposes
public virtual DateTime Time
{
get
{
return DateTime.MinValue.AddYears(1);
}
}
}
public class InboundFlow : Flow
{
}
public class OutboundFlow : Flow
{
}
public abstract class Fluent
{
IList<Flow> _flowCollection;
public virtual IList<Flow> FlowCollection
{
get { return _flowCollection; }
set { _flowCollection = value; }
}
private double _initialBaseflow;
public virtual double InitialBaseflow
{
get { return _initialBaseflow; }
set { _initialBaseflow = value; }
}
public Fluent()
{
FlowCollection = new List<Flow>();
}
}
public class Affluent : Fluent
{
//public new virtual IList<InboundFlow> FlowCollection { get; set; }//Keep the property polymorphic
public Affluent()
{
FlowCollection = new List<Flow>();
}
}
public class Effluent : Fluent
{
//public new virtual IList<OutboundFlow> FlowCollection { get; set; }
public Effluent()
{
FlowCollection = new List<Flow>();
}
}
class Program
{
public static DateTime SOME_DATE { get { return DateTime.MinValue; } }
public static DateTime SOME_OTHER_DATE { get { return DateTime.Now; } }
static void Main(string[] args)
{
var inbound = new InboundFlow();
var inbound2 = new InboundFlow();
var outbound = new OutboundFlow();
var a = new Affluent();
a.FlowCollection.Add(inbound);
a.FlowCollection.Add(inbound2);
FindInitialBaseflow(a);
}
private static void FindInitialBaseflow(Fluent fluent)
{
var linqFluent = fluent;
var flows = linqFluent.FlowCollection.ToList().FindAll(
flow =>
flow.Time >= SOME_DATE &&
flow.Time < SOME_OTHER_DATE);
var initialBaseflow = flows.Average(flow => flow.Value);
fluent.InitialBaseflow = Math.Round(initialBaseflow, 5);
}
}
}