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);
        }
    }
}
Другие вопросы по тегам