【问题标题】:C# Inheritance, new modifier and genericsC# 继承、新修饰符和泛型
【发布时间】:2013-08-13 15:12:40
【问题描述】:

我很难找到正确的方法来解决这个问题:

我的数据结构:

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

我的问题是在 linq 方法中调用“linqfluent.FlowCollection”会调用 Fluent 的 FlowCollection 基类,它为空。

如何强制使用孩子的财产?谢谢!

【问题讨论】:

  • new 用新版本覆盖属性,您需要覆盖它。但是你会遇到协方差问题。
  • 你能告诉我你是如何调用 FindInitialBaseflow 的吗? FindInitialBaseflow(ref affluentobject) 是否调用基类集合..
  • @Ashley John : FindInitialBaseflow(ref affluent) 和 affluent 属于 Affluent 类型

标签: c# linq generics inheritance name-hiding


【解决方案1】:

您需要将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,因此无需通过引用传递它。它已经是一个类,所以它本身就是一个引用;调用者将观察到引用对象的变化。

【讨论】:

    【解决方案2】:

    泛型是错误的工具。您应该使用多态性来确保根据类型调用正确的实现。

    例如:

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

    【讨论】:

    • 但是子类型中的属性隐藏了FlowCollection 属性,它们不会覆盖它,所以在这种情况下你不会得到多态行为。这是 OP 用他的代码观察到的问题,您的更改不会改变这种行为。
    • @Servy - 仅仅因为 OP 没有正确设置他的属性并不意味着他不能或不应该使用多态性。泛型不适合 OP 所要求的多态行为。
    • 如果您希望这是一个答案,您需要向他展示如何“正确设置他的属性”。请注意,他的属性并非都解析为相同的类型。多态行为将要求所有集合具有相同的编译时类型。由于这是不可能的,因此泛型显然是要走的路。如果您可以展示一个实际有效的多态解决方案,而不是一个具有与 OP 的代码完全相同的问题的解决方案,那么无论如何,我很乐意看到它。
    • 泛型帮助我消除代码重复,因为我必须对 Affluent 和 Effluent 属性进行相同的计算
    • @PhillyQc - 对我来说有代码味道。我建议向子 Affluent/Effluent 添加新行为,而不是隐藏基本实现并消除多态性。
    猜你喜欢
    • 1970-01-01
    • 2010-10-14
    • 2016-01-29
    • 1970-01-01
    • 2017-08-17
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多