4

I have interface and 3 classes that derives it, but how use 1 variable for each one type?

  public interface IBuilder<T> where T: System.IConvertible{}

  public class SimpleBuilder :  IBuilder<SimpleCollagePatterns>{}

  public class CreativeBuilder : IBuilder<CreativeCollagePatterns>{}

  public class ShapeBuilder : IBuilder<ShapeCollagePatterns>{}

And I need create instance of class when it`s necessary

I have IBuilder<IConvertible> currentBuilder variable, but I cannot create instance of any Builder

this.currentBuilder = new SimpleBuilder(); //Doesn`t work

Only if I change IBuilder<IConvertible> currentBuilder to IBuilder<SimpleCollagePatterns> currentBuilder or atother type I can create this type of Builder, but I need to have abulity create any type

SmiLe
  • 311
  • 2
  • 13
  • Interfaces aren't classes so you cannot create instances of them. The only way to create instances is by the way you chose, that is defining classes that implement the interface e.g. SimpleCollagePatterns. Also interfaces do not contain any code that do something specific so why would you like to create instances of them at all? Why not change IBuilder to a parent class? – jambonick Nov 23 '16 at 09:40
  • The problem you're facing is that you have `I` and `I` (ie. the same generic interface used twice with different generic parameters), and there is a inheritance hierarchy between `T1` and `T2`. The problem is that there is no inheritance hierarchy between `I` and `I`. You can use covariance and contravariance to handle this depending on which way the inheritance goes, but this places very specific restrictions on the usage of the types, likely not what you want. In short, this is not possible to do. – Lasse V. Karlsen Nov 23 '16 at 10:16

5 Answers5

0

I faced the same scenario. But ended up to find there is no solution for this. It is not possible to create object of any type in run time. As you said, changing IBuilder currentBuilder to IBuilder currentBuilder is the only way to create object for SimpleBuilder type. Similarly for other cases.

Sethu Bala
  • 457
  • 3
  • 17
0

If you really want to use one variable for all scenarios, you can always define it as a simple object to hold on to the instance and use the IS operator to see which type it really is when necessary like this:

if (this.currentBuilder is SimpleBuilder) { }

Nathan Swannet
  • 220
  • 3
  • 12
  • But I cant change it. I need change type – SmiLe Nov 23 '16 at 09:56
  • Why can't you change the declaration of currentBuilder? Is it not your own code? So replace IBuilder currentBuilder to object currentBuilder. – Nathan Swannet Nov 23 '16 at 09:58
  • How to do this? – SmiLe Nov 23 '16 at 09:59
  • Replace IBuilder currentBuilder to object currentBuilder – Nathan Swannet Nov 23 '16 at 10:00
  • hmm, Im try but always when I use it I need to cast to necessary type? – SmiLe Nov 23 '16 at 10:00
  • That's the downside of it. I don't like it as an elegant solution but it meets your requirements for holding on to 1 variable for all the classes. However, that would also be the case if you declare it as IBuilder. Then you also have to cast it to have access to the functionality of your classes. Aren't you missing a generic parent class named CollagePatterns with shared methods and properties? – Nathan Swannet Nov 23 '16 at 10:04
0

I believe you need to make your IBuilder covariant, see https://msdn.microsoft.com/en-us/library/dd997386(v=vs.110).aspx

public interface IBuilder<out T>

A Jackson
  • 2,776
  • 7
  • 34
  • 45
0

What you want to read about is called covariance, which is denoted by keyword out beside a generic type parameter in the interface declaration.

public interface IBuilder<out T> where T: System.IConvertible{}

This will allow you to declare a variable of type IBuilder<IConvertible> and assign eg. SimpleBuilder to it, you just need to be aware of what consequences applying covariance has, the most important being, in plain words, generic type can only be used as return type of the type members.

If you can't change the IBuilder interface - there is no option to create a variable for all cases.

For further info you can research SO, eg. Difference between Covariance & Contra-variance or read at the source, eg. https://msdn.microsoft.com/en-us/library/mt654055.aspx.

Community
  • 1
  • 1
kiziu
  • 1,111
  • 1
  • 11
  • 15
0

If you don't need public access to the generic type

In particular if SimpleCollagePatterns is used exclusivly by the class that implements that particular interface (In this case ShapeBuilder), then you don't need covariance.

There is a simple design pattern that relies on inheritance alone.

For example:

public interface IBuilder {
    void Build();
}

public interface IBuilder<T> : IBuilder {
    T BuildParameter {get;}
}

Note: The BuildParameter corresponds to your BuildCollagePattern

Then you can implement the interface as such:

public class SpecificBuilder : IBuilder<Int32> {
    // The specific constructor
    public SpecificBuilder(int param) { BuildParameter = param; }

    // Implement from IBuilder
    public void Build() {
        System.Console.WriteLine("Building with Int32: " + BuildParameter);
    }

    // Implement from IBuilder<T>
    public Int32 BuildParameter {get; private set;}
}

Then you can pass around any IBuilder<T> as an IBuilder

public class Program {
    public static void Main() {
        SpecificBuilder builder = new SpecificBuilder(42);

        // SpecificBuilder implements IBuilder<Int32>
        // Build accepts any IBuilder
        // So this is legal:
        Build(builder);
    }

    //
    // Note this method accepts anything that inherits from IBuilder
    // 
    public static void Build(IBuilder builder) {

        builder.Build();

        // If you'd need access to the BuildParameter of IBuilder<T>
        // then this pattern fails you here.
        // Unless of course you want to check for types and cast
    }
}

As noted before: Once you need access to the generic type outside of the class thats implementing the IBuilder<T> you'd need to start differentiating types and cast them accordingly. Not so elegant.

But you can design lot's of stuff this way and maintain extensibility. I bring this up, as I have used this pattern to implement image filters myself and your BuildCollagePattern might be very much realizable with this design pattern.

Community
  • 1
  • 1
MrPaulch
  • 1,423
  • 17
  • 21