Click here to Skip to main content
14,694,402 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
My question is, I think, perfectly illustrated by the following code (which does not compile as it stands):

public abstract class MyBaseBusinessObjectClass
{
    public abstract string BusinessObjectName { get; }
}

public class BusinessObjectOne : MyBaseBusinessObjectClass
{
    public override string BusinessObjectName => "One";

    public string FunctionSpecificToOne => "1";
}

public class BusinessObjectTwo : MyBaseBusinessObjectClass
{
    public override string BusinessObjectName => "Two";

    public string FunctionSpecificToTwo => "2";
}


public abstract class MyBaseLogicClass
{
    public abstract void PrintParamName<T>( T param );
}

public class LogicHandlingOnes : MyBaseLogicClass
{
    public override void PrintParamName<BusinessObjectOne>( BusinessObjectOne param )
    {
        Console.WriteLine( $"BusinessObjectOne.PrintParamName() returned '{param.BusinessObjectName}'." );

        // The following line does not compile.
        string s = ((BusinessObjectOne)param).FunctionSpecificToOne();
    }
}

public class LogicHandlingTwo : MyBaseLogicClass
{
    public override void PrintParamName<BusinessObjectTwo>( BusinessObjectTwo param )
    {
        // The following line does not compile.
        Console.WriteLine( $"BusinessObjectTwo.PrintParamName() returned '{param.BusinessObjectName}'." );

        // The following line does not compile.
        string s = ((BusinessObjectTwo)param).FunctionSpecificToTwo();
    }
}


This gives the following errors on compilation:

1>Program.cs(35,77,35,95): error CS1061: 'BusinessObjectOne' does not contain a definition for 'BusinessObjectName' and no accessible extension method 'BusinessObjectName' accepting a first argument of type 'BusinessObjectOne' could be found (are you missing a using directive or an assembly reference?)
1>Program.cs(47,77,47,95): error CS1061: 'BusinessObjectTwo' does not contain a definition for 'BusinessObjectName' and no accessible extension method 'BusinessObjectName' accepting a first argument of type 'BusinessObjectTwo' could be found (are you missing a using directive or an assembly reference?)
1>Program.cs(38,42,38,63): error CS1061: 'BusinessObjectOne' does not contain a definition for 'FunctionSpecificToOne' and no accessible extension method 'FunctionSpecificToOne' accepting a first argument of type 'BusinessObjectOne' could be found (are you missing a using directive or an assembly reference?)
1>Program.cs(50,42,50,63): error CS1061: 'BusinessObjectTwo' does not contain a definition for 'FunctionSpecificToTwo' and no accessible extension method 'FunctionSpecificToTwo' accepting a first argument of type 'BusinessObjectTwo' could be found (are you missing a using directive or an assembly reference?)

If I stick with the version of the code with the constrained parameter, as shown below, it compiles fine if I comment out the two lines that attempt to cast the parameters to their derived types.

I am obviously failing to understand something fundamental about generic parameters but it is the compiler message that confuses me. It uses the 'correct' derived type in the message and then tells me it can't find the function contained in that class.

Is there something simple I'm not getting here or do I need to find my 'Beginning C#' book?

I would also appreciate more general comments on the architecture. I am aware it is less than ideal but the real code is too big to refactor before my next delivery deadline so I just need to get something working for now.

What I have tried:

I have tried constraining the generic type as follows:

public abstract class MyBaseLogicClass
{
    public abstract void PrintParamName<T>( T param ) where T : MyBaseBusinessObjectClass;
}


Which gets rid of two errors but leaves:

1>------ Build started: Project: ConsoleApp1, Configuration: Debug Any CPU ------
1>Program.cs(38,42,38,63): error CS1061: 'BusinessObjectOne' does not contain a definition for 'FunctionSpecificToOne' and no accessible extension method 'FunctionSpecificToOne' accepting a first argument of type 'BusinessObjectOne' could be found (are you missing a using directive or an assembly reference?)
1>Program.cs(50,42,50,63): error CS1061: 'BusinessObjectTwo' does not contain a definition for 'FunctionSpecificToTwo' and no accessible extension method 'FunctionSpecificToTwo' accepting a first argument of type 'BusinessObjectTwo' could be found (are you missing a using directive or an assembly reference?)
Posted
Updated 17-Jan-20 2:36am
Comments
Richard MacCutchan 17-Jan-20 8:46am
   
That is because you have defined FunctionSpecificToOne and FunctionSpecificToTwo as properties rather than functions.

You could define MyBaseLogicClass as
public abstract class MyBaseLogicClass<T>
   where T : MyBaseBusinessObjectClass
{
   public abstract void PrintParamName( T param );
}

public class LogicHandlingOnes : MyBaseLogicClass<BusinessObjectOne>
{
   // ...
}

public class LogicHandlingTwo : MyBaseLogicClass<BusinessObjectTwo>
{
   // ...
}
   
The generic type "T" is defined when you create the class so it has to go on the class definition

public abstract class MyBaseLogicClass<T>
{
    public abstract void PrintParamName(T param);
}

public class LogicHandlingOnes : MyBaseLogicClass<BusinessObjectOne>
{
    public override void PrintParamName(BusinessObjectOne param)
    {

        Console.WriteLine($"BusinessObjectOne.PrintParamName() returned '{param.BusinessObjectName}'.");

        string s = param.FunctionSpecificToOne;
    }
}

public class LogicHandlingTwo : MyBaseLogicClass<BusinessObjectTwo>
{
    public override void PrintParamName(BusinessObjectTwo param)
    {
        Console.WriteLine($"BusinessObjectTwo.PrintParamName() returned '{param.BusinessObjectName}'.");

        string s = param.FunctionSpecificToTwo;
    }
}


You could also define the baselogic class like

public abstract class MyBaseLogicClass<T> where T: MyBaseBusinessObjectClass


Getting back to your original concept, if the generic type is define on the function then the code that calls the function defines the type.

public abstract class MyBaseLogicClass<T>
{
    public abstract void PrintParamName(T param);

    public abstract void GenericFunction<R>(R param) where R: MyBaseBusinessObjectClass;
}

public class LogicHandlingOnes : MyBaseLogicClass<BusinessObjectOne>
{
    public override void GenericFunction<R>(R param)
    {
        // it is the creator that defines the type so we don't know what "R" is
        // but we know it has to inherit from MyBaseBusinessObjectClass so we can use
        // any methods available on MyBaseBusinessObjectClass

        Console.WriteLine(param.BusinessObjectName);
    }
}


calling code;


LogicHandlingOnes ones = new LogicHandlingOnes();

// the calling code defines what "R" is
ones.GenericFunction<BusinessObjectOne>(new BusinessObjectOne()); // outputs "One"
ones.GenericFunction<BusinessObjectTwo>(new BusinessObjectTwo()); // outputs "Two"
   
Comments
Patrick Skelton 18-Jan-20 4:26am
   
I think this 'the code that calls the function defines the type' is the key piece of information I was failing to understand.

Thank you both!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900