Search code examples
c#linqgenericsinheritancename-hiding

C# Inheritance, new modifier and generics


I'm having a hard trying to find to correct approach to this :

My data structures :

public abstract class Flow
{
    public virtual double Value { get; set; }
    public virtual DateTime Time { get; set; }
}

public class InboundFlow : Flow
{
}

public class OutboundFlow : Flow
{
}

My business objects containing collections of these data structures

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

The generic method I'm trying to use :

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

My problem is that calling "linqfluent.FlowCollection" in the linq method calls for the base class Fluent's FlowCollection, which is null.

How can I force the use of the child's property instead? Thanks!


Solution

  • You need to make the collection within Fluent generic so that the classes that inherit from it can specify the type:

    public class Fluent<T>
        where T : Flow
    {
        public IList<T> FlowCollection { get; set; }
        public double InitialBaseflow { get; set; }
    }
    

    Once you have that you don't even need sub classes of Flow, you can just make it concrete.

    Your use of it would be easily modified to fit this model:

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

    Also note that since you're not setting fluent in this method, there is no need to pass it by reference. It's already a class, so it is itself a reference; mutations of the referenced object will be observed by the caller.