locked
Can new Constraint of Generic Class has parameters? RRS feed

  • Question

  • I use Generic Class in two relevant base classes, one dependence on another in '1 to *'. I call them THigher and TLower here.

    When I instantiate a THigher, relative TLowers should be instantiate also. So TLower requires a new Constraint as:

    where TLower : new()

    The problem is, how if I wanna pass parameters in constructor of TLower?

    I wish there's something like:

    where TLower : new(string, int)

    Then I might be able to do as:

    var lower = new TLower("Hello World!", 123);

    but

    where TLower : ICreateTLower
    interface TCreateLower<TLower>
    {
        TLower Create(string saySomething, int itDoesntMatter);
    }

    I think it's very ugly to do so which makes code much more and not easy to maintain.

    Is there a solution or an elegant trick?

    • instantiate

    =====================(I'm a parting line)=====================

    Sorry for not made myself clear. And thanks for Mike Danes and PaulLinton who notice me there're mistakes in what I mentioned.

    Actually, what I want is something like:

    class MyClass<THigher, TLower>
        where TLower : new(string)
    {
        public MyClass(THigher higher)
        {
            this.Higher = higher;
            this.Lowers = new string[]
            {
                "Hello", "World", "!"
            }.Select(item => new TLower(item));
        }
    
        THigher Higher { get; set; }
    
        IEnumerable<TLower> Lowers { get; set; }
    }

    And you'll find that there're error when compile where 'new(string)' and 'new TLower(item', because there's only New Constraint with no parameter function for Generic Class.

    So, for requirements like this, we could do only like:

    class MyClass<THigher, TLower, TLowerFactory>
        where TLowerFactory : IFactory<TLower>, new()
    {
        public MyClass(THigher higher)
        {
            this.Higher = higher;
    
            var lowerFactory = new TLowerFactory();
    
            this.Lowers = new string[]
            {
                "Hello", "World", "!"
            }.Select(item => lowerFactory.New(item));
        }
    
        THigher Higher { get; set; }
    
        IEnumerable<TLower> Lowers { get; set; }
    }
    
    interface IFactory<T>
    {
        T New(string request);
    }

    That's what i don't wanna see at last which force me to handler at least a Factory class more. It's just not too 'elegant'.

    Also, you're not able to do as:

    class MyClass<THigher, TLower>
        where TLower : LowerBase, new()
    {
        public MyClass(THigher higher)
        {
            this.Higher = higher;
    
            this.Lowers = new string[]
            {
                "Hello", "World", "!"
            }.Select(item => TLower.New(item));
        }
    
        THigher Higher { get; set; }
    
        IEnumerable<TLower> Lowers { get; set; }
    }
    
    abstract class LowerBase
    {
        public abstract static LowerBase New(string request);
    }

    Compile error shows ''TLower' is a 'type parameter', which is not valid in the given context' where 'Lower.New(item)'.

    So, do you have any pretty solution?

    • Edited by Indream Luo Tuesday, April 15, 2014 11:37 AM Try to make myself more clear
    Monday, April 14, 2014 7:37 AM

Answers

  • An alternative to a factory classes are delegates:

    class MyClass<THigher, TLower> {
        public MyClass(THigher higher, Func<string, TLower> factory)
        {
            this.Higher = higher;
            this.Lowers = new string[] {
                "Hello", "World", "!"
            }.Select(factory);
        }
    
        THigher Higher { get; set; }
        IEnumerable<TLower> Lowers { get; set; }
    }
    
    class Higher {
    }
    
    class Lower {
        public Lower(string s) {
        }
    }
    
    class Program {
        static void Main() {
            var higher = new Higher();
            new MyClass<Higher, Lower>(new Higher(), s => new Lower(s));
        }
    }

    And as a convenience you can add this:

    static class MyClass {
        public static MyClass<THigher, TLower> New<THigher, TLower>(THigher higher, Func<string, TLower> factory) {
            return new MyClass<THigher, TLower>(higher, factory);
        }
    }

    Which allows you to create the class without specifying the type arguments:

    MyClass.New(higher, s => new Lower(s));


    • Edited by Mike Danes Tuesday, April 15, 2014 3:59 PM fix code
    • Marked as answer by Barry Wang Thursday, April 24, 2014 2:20 AM
    Tuesday, April 15, 2014 3:59 PM
  • I find 4 solutions from last day.

    • Use Factory Pattern
    • Inject by the constructor of Generic Class
    • Activator.CreateInstance
    • Initialize() after instantiate

    When I wanted to have a static method of base class of the type parameter, I found that I'm also not able to use the static method from type parameter.

    I think at the end we'll found it's the weekness of Generic Class of C#.

    • Marked as answer by Barry Wang Thursday, April 24, 2014 2:20 AM
    Wednesday, April 16, 2014 6:42 AM

All replies

  • There are no constraints for constructors with parameters. I doubt that you can use something like TLower : ICreateLower - that assumes that you already have an object of type TLower on which you can call Create to create another object of type TLower.

    I'm not sure what a good solution would be, it depends on what you're doing and that's not very clear from your post. You mentioned something about base classes, if you derive from the generic class then you could add "abstract TLower Create()" to the generic class and override this in the derived classes.

    Monday, April 14, 2014 8:26 AM
  • I am not sure that I understand what you are trying to do but it sounds to me that you would be better to pass a factory method which can create a TLower rather than just a type parameter.  If you pass a parameter of type

    Func<string, int, TLower)

    then you can make new TLower objects by calling the method with appropriate parameters.


    Paul Linton

    Monday, April 14, 2014 10:43 AM
  • You lighted me a spark, so I did some test and append more to make myself more clear.

    As so I appended, static Method is not able to be called from type parameter of a Generic Class.

    Tuesday, April 15, 2014 11:41 AM
  • Sorry for didnot make myself clear, I mean that after I reviewed the question.

    Factory is a common Pattarn in such situation which is too much that I just want it simpler as only a constructor. But seems that C# doesn't support that currently.

    Tuesday, April 15, 2014 11:48 AM
  • An alternative to a factory classes are delegates:

    class MyClass<THigher, TLower> {
        public MyClass(THigher higher, Func<string, TLower> factory)
        {
            this.Higher = higher;
            this.Lowers = new string[] {
                "Hello", "World", "!"
            }.Select(factory);
        }
    
        THigher Higher { get; set; }
        IEnumerable<TLower> Lowers { get; set; }
    }
    
    class Higher {
    }
    
    class Lower {
        public Lower(string s) {
        }
    }
    
    class Program {
        static void Main() {
            var higher = new Higher();
            new MyClass<Higher, Lower>(new Higher(), s => new Lower(s));
        }
    }

    And as a convenience you can add this:

    static class MyClass {
        public static MyClass<THigher, TLower> New<THigher, TLower>(THigher higher, Func<string, TLower> factory) {
            return new MyClass<THigher, TLower>(higher, factory);
        }
    }

    Which allows you to create the class without specifying the type arguments:

    MyClass.New(higher, s => new Lower(s));


    • Edited by Mike Danes Tuesday, April 15, 2014 3:59 PM fix code
    • Marked as answer by Barry Wang Thursday, April 24, 2014 2:20 AM
    Tuesday, April 15, 2014 3:59 PM
  • I find 4 solutions from last day.

    • Use Factory Pattern
    • Inject by the constructor of Generic Class
    • Activator.CreateInstance
    • Initialize() after instantiate

    When I wanted to have a static method of base class of the type parameter, I found that I'm also not able to use the static method from type parameter.

    I think at the end we'll found it's the weekness of Generic Class of C#.

    • Marked as answer by Barry Wang Thursday, April 24, 2014 2:20 AM
    Wednesday, April 16, 2014 6:42 AM