locked
C# Generics - Can call subclass methods but not base class methods without adding dependency

    Question

  • Hi, i'm sure this has been answered before but i can't seem to come up with the right keywords to search with :)



    I created a test solution in VS2008 to demonstrate the problem i'm having, which has the following projects:

    TypeClassLibrary - has dependency on nothing but the standard stuff you get when you create a blank class library project

    LowerLayer - has dependency on TypeClassLibrary

    MiddleLayer - has dependency on LowerLayer and TypeClassLibrary

    GenericsTestGUILayer - has dependency on MiddleLayer and TypeClassLibrary


    TypeClassLibrary
    ================

    Has one class declaration, which is as follows -

    namespace TypesClassLibrary
    {
        public class LowerLayerClass
        {
            public void testMethod()
            {

            }
        }
    }


    LowerLayer (Dependent on TypeClassLibrary)
    ==========

    Also has one class declaration, which inherits from the single class declared in TypeClassLibrary -

    namespace LowerLayer
    {
        public class InterestingLowerLayerClass : LowerLayerClass
        {
        }
    }


    MiddleLayer (Dependent on LowerLayer and TypeClassLibrary)
    ===========

    Has 2 class declarations -

    namespace MiddleLayer
    {
        public class GenericsTestBaseClass<T> where T : LowerLayerClass, new()
        {
            public static void BaseClassDoSomethingStatic()
            {

            }
            public void BaseClassDoSomething()
            {
            }
        }

        public class GenericsTestSubClass : GenericsTestBaseClass<InterestingLowerLayerClass>
        {
            public static void SubClassDoSomethingStatic()
            {
                
            }
            public void SubClassDoSomething()
            {

            }

        }
    }

    GenericsTestGUILayer (Dependent on MiddleLayer and TypeClassLibrary)
    ====================
    namespace GenericsTest
    {
        class Program
        {
            static void Main(string[] args)  //I created this as a console app
            {
                GenericsTestSubClass.SubClassDoSomethingStatic();
                //GenericsTestSubClass.BaseClassDoSomethingStatic();// -- error!!! can't resolve type argument?

                GenericsTestSubClass g = new GenericsTestSubClass();
                g.SubClassDoSomething();
                //g.BaseClassDoSomething();  //-- error!!!
            }
        }
    }


    If you uncomment either of the lines marked with 'error!!!' above, you get the following compilation errors -

    Error    3    'LowerLayer.InterestingLowerLayerClass' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'MiddleLayer.GenericsTestBaseClass<T>'    c:\build\GenericsTest\MiddleLayer\bin\Debug\MiddleLayer.dll   

    GenericsTestGUILayer Error    2    The type 'LowerLayer.InterestingLowerLayerClass' cannot be used as type parameter 'T' in the generic type or method 'MiddleLayer.GenericsTestBaseClass<T>'. There is no boxing conversion from 'LowerLayer.InterestingLowerLayerClass' to 'TypesClassLibrary.LowerLayerClass'.   

    c:\build\GenericsTest\MiddleLayer\bin\Debug\MiddleLayer.dll    GenericsTestGUILayer Error    1    The type 'LowerLayer.InterestingLowerLayerClass' is defined in an assembly that is not referenced. You must add a reference to assembly 'LowerLayer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.    C:\build\GenericsTest\GenericsTest\Program.cs    14    GenericsTestGUILayer



    There's no problem with calling methods, static or non-static, in the sub class.  It's only when you call the base class methods that you'll have an issue.

    There's 2 ways around this that i know of -

    1) Add a dependency on LowerLayer in the GenericsTestGUILayer - something I don't want to do.

    2) Add wrapper methods to GenericsTestSubClass so that they can call their appropriate base class methods, like this:

            public static void SubClassCallBaseClassDoSomethingStatic()
            {
                GenericsTestBaseClass<InterestingLowerLayerClass>.BaseClassDoSomethingStatic();
            }
            public void SubClassCallBaseClassDoSomething()
            {
                base.BaseClassDoSomething();
            }

    which would be painful given that i have many sub classes to implement.



    The reality of my situation that that i'm using a modified version of this - http://code.msdn.microsoft.com/multitierlinqtosql

    The difference between what i'm doing and what 'rbates' has done is that i've seperated the generated DataContext class into a lower layer so that it's
    not accessable by the GUI layer (that idea came from http://www.codeproject.com/KB/architecture/EnterpriseApplicationArch.aspx).  So think of TypesClassLibrary as the linq-to-sql library which has the DataContext type, LowerLayer as my DAL which has my generated DataContext class defined, the MiddleLayer where the "Entity Controller classes" from that rbates article lives, and the GenericsTestGUILayer as the GUI.


    What I gathered is that the compiler needs to resolve the type when calling GenericsTestSubClass.BaseClassDoSomethingStatic() in the gui layer but fails to do so, even though i'm calling it off GenericsTestSubClass, and GenericsTestSubClass should sort that out for me anyway.


    I hope all of that makes sense and if anyone can show me a better way around this issue it'll be greatly appreciated!

     
    • Edited by Frank Tzanabetis Monday, September 01, 2008 5:08 AM fixed grammatical error
    Monday, September 01, 2008 5:05 AM

Answers


  • As is always the case, when you think something through, a simple solution presents itself :)

    Ok, i added another parameterised type in the generics class to better reflect what i'm trying to do in my real project -



    namespace MiddleLayer
    {
        public class GenericsTestBaseClass<T, TEntity> where T : LowerLayerClass, new() where TEntity : class
        {
            public static void BaseClassDoSomethingStatic()
            {

            }
            public void BaseClassDoSomething()
            {
            }
        }

        public class GenericsTestMiddleClass<TEntity> : GenericsTestBaseClass<InterestingLowerLayerClass, TEntity> where TEntity : class
        {
            public static void MidClassDoSomethingStatic()
            {
                GenericsTestBaseClass<InterestingLowerLayerClass, TEntity>.BaseClassDoSomethingStatic();
            }
            public void MidClassDoSomething()
            {
                base.BaseClassDoSomething();
            }
        }
        public class GenericsTestTEntityTest : GenericsTestMiddleClass<TEntityTester>
        {
            public static void SubClassDoSomethingStatic()
            {

            }
            public void SubClassDoSomething()
            {

            }
        }

    }



    Notice i've inserted an 'in-between' class, to wrap the stuff i wanna call in the base class.

    So now i can call the base class stuff by using the in-between wrapper, and i don't have to implement it over and over in all the subclasses, and i don't

    have to have a lowerlayer dependency in the GUI layer.

    GenericsTestTEntityTest.MidClassDoSomethingStatic(); // no problem - Hooray for zo1dberg!!!

    GenericsTestTEntityTest g = new GenericsTestTEntityTest();
    g.MidClassDoSomething(); // no problem - Hooray for zo1dberg!!!

    Crisis averted.

    Still, if anyone knows of a more elegant solution, i'd love to see it.

    Monday, September 01, 2008 6:06 AM