2

I have one interface InterfaceBase and some interfaces derived from it Interface1, Interface2. Next I have classes that are implementing the InterfaceX interfaces, not the base one.

Now, i am beginner in generics and so many new approaches in this made great mess in my head :( . I want to create factory (static class) where I call something like

Interface1 concrete1 = Factory.Get<Interface1>();

Here is my (sample) implementation of factory, that does not work:

  public static class Factory {

    public static T Get<T>() where T: InterfaceBase{

      Type type = typeof(T);

      //return new Concrete1() as T; // type T cannot be used with the as
      //return new Concrete1() as type; //type not found
      //return new Concrete1(); // cannot implicitly convert
      //return new Concrete1() as InterfaceBase; //cannot convert IBase to T
      //return new Concrete1() as Interface1; //cannot convert Interface1 to T
    }
  }

What I want to achieve is hide the classes (they are webservice handlers) from the rest of the application to exchange them lightly. I wanted use the factory as the classes will be singletons and they will be stored in Dictionary inside the factory, so the factory can spread them across the application through this method, but as interfaces.. Maybe i am not using the constraints correctly am I doing smthing wrong? is my approach bad? can be there something better, maybe the whole architecture shoul be reworked? diagram to show better the architecture. The factory is not in it

Zavael
  • 2,383
  • 1
  • 32
  • 44

5 Answers5

7

Methinks what you are looking for is a "Poor-Man Dependency Injection". I guess you should use a real IoC container for that, there are a lot of options (Unity, Castle Windsor, Ninject...).

But anyway, If you insist in doing it by yourself, go with what @Sergey Kudriavtsev is recomending. Just make sure that, for each interface, you return the proper concrete class. Something like this:

public interface InterfaceBase { }
public interface Interface1 : InterfaceBase { }
public interface InterfaceX : InterfaceBase { }

public class Concrete1 : Interface1 { }
public class ConcreteX : InterfaceX { }

public static class Factory
{
    public static T Get<T>()
        where T : InterfaceBase
    {
        if (typeof(Interface1).IsAssignableFrom(typeof(T)))
        {
            return (T)(InterfaceBase)new Concrete1();
        }
        // ...
        else if (typeof(InterfaceX).IsAssignableFrom(typeof(T)))
        {
            return (T)(InterfaceBase)new ConcreteX();
        }

        throw new ArgumentException("Invalid type " + typeof(T).Name, "T"); // Avoids "not all code paths return a value".
    }
}

And you call it by passing the interface reference to the factory:

var instance = factory.Get<Interface1>();
rsenna
  • 11,775
  • 1
  • 54
  • 60
  • This doesn't work. It's the same as @Sergey Kudriavtsev proposed. – Diego Feb 09 '12 at 15:37
  • 1
    +1 for identifying the "real" solution (inversion of control) rather than just the immediate problems described in the question. The broad description of the OP's requirements ("hide the classes ... and exchange them lightly") is a good statement of the purpose of IoC. – phoog Feb 09 '12 at 15:38
  • But after leaving my first comment, I realized that this solution won't compile because Concrete1 is not known to derive from InterfaceBase. The runtime type check `typeof(Interface1).IsAssignableFrom(typeof(T))` doesn't fix that because the compiler doesn't take it into account. – phoog Feb 09 '12 at 15:42
  • @Diego: it does work, I don't know why it doesn't work for the OP though. And yes, it is an expansion of Sergey's answer, that's why I'm citing him (and also +1nd his answer). – rsenna Feb 09 '12 at 15:42
  • I'm trying to compile this but it doesn't compile. That's why I said it doesn't work. – Diego Feb 09 '12 at 15:44
  • @Diego: OK, I'll look into it. – rsenna Feb 09 '12 at 15:45
  • @Diego see my answer for one reason why it doesn't compile. – phoog Feb 09 '12 at 15:50
  • @Diego: Sorry, you were right. But basically the only thing missing was a intermediary cast to `InterfaceBase`. See my edited answer. – rsenna Feb 09 '12 at 15:53
  • @phoog you are right your edit2 explains clearly why this approach doesn't compile. Thanks! – Diego Feb 09 '12 at 15:55
  • @rsenna now it works fine :) thanks! But I think this solution doesn't scale well, for every new sub-interface you add you need to add a new branch to the if. – Diego Feb 09 '12 at 16:02
  • @rsenna The cast to InterfaceBase could also be a cast to `object`. By (upcasting and) downcasting, you're telling the compiler to insert a runtime type check, so it no longer complains at compile time about the cast. I'm now curious whether the `as` solution or the cast-up-then-down solution is more or less efficient -- or perhaps it generates the same IL? If I find some time later today, I'll check. – phoog Feb 09 '12 at 16:03
  • 1
    @Diego You could define the interface vs concrete type mapping in your App.Config file, and then read it in the factory method. That's basically the Unity approach, for instance. But I won't add that to my answer: I do have a real work you know, and I already use Unity for that kind of thing. :) – rsenna Feb 09 '12 at 16:13
  • @rsenna, you are right, that would be a better way to do this :D – Diego Feb 09 '12 at 16:16
  • @phoog I understand your point, and maybe there is a better option, if you are really concerned about saving some more nanoseconds... Just remember that the OP wants to instantiate **web-service handlers**: which means that he has bigger bottlenecks to worry about. :) – rsenna Feb 09 '12 at 16:28
  • @rsenna the IL/efficiency question was more of academic interest about the mechanics of the language than of practical interest. Clearly if there is a difference in performance it will be infinitesimal compared to any other performance factors. – phoog Feb 09 '12 at 16:30
2

This should work:

return (T)(new Concrete1());

Also the code for calling factory method should be like this:

Interface1 concrete1 = Factory.Get<Interface1>();
Sergey Kudriavtsev
  • 10,328
  • 4
  • 43
  • 68
  • +1 But I'd use `return new Concrete1() as T;` and check for `null` in calling code. – Yuck Feb 09 '12 at 15:17
  • sergey - no it works not, cannot convert Concrete1 to T, but thanks for the factory calling :) corrected – Zavael Feb 09 '12 at 15:19
  • @Zavael Does `Concrete1` implement `InterfaceBase`? – Yuck Feb 09 '12 at 15:20
  • 1
    @Yuck: `as` will not work here without some adjustments; please take a look at: http://stackoverflow.com/questions/693463/operator-as-and-generic-classes – Sergey Kudriavtsev Feb 09 '12 at 15:23
  • no, it implements Interface1 directly, which derives from InterfceBase.. its written in Q – Zavael Feb 09 '12 at 15:23
  • sergey - but i must to add in where clause the "class" no? and it means then that class should be passed in method or am I wrong? – Zavael Feb 09 '12 at 15:24
  • 1
    What comiler will say about `return (T)((InterfaceBase)(new Concrete1()));` ? – Sergey Kudriavtsev Feb 09 '12 at 15:26
  • @Zavael Interfaces don't *derive* from each other. That's what you're doing wrong. They merely describe contracts. – Yuck Feb 09 '12 at 15:27
  • 2
    @Yuck but interfaces can inherit from one another, and "derive from" is a reasonable way to describe that relationship. How else would you describe `interface IEnumerable : IEnumerable { ... }`? – phoog Feb 09 '12 at 15:34
  • 1
    Oops, Correction to earlier (deleted) comment: @Zavael "class" actually means "reference type", so it includes interfaces as well as classes. Similarly, "struct" means "value type", so it includes enums. (Earlier comment incorrectly said that "struct" means "reference type" too!) – phoog Feb 10 '12 at 00:31
  • @SergeyKudriavtsev - it does compile, but it seems to me dangerous as the compiler enabled StringBuilder sb = (StringBuilder)((object)new Concrete1()); too... PS: how to write code in comments to have that gray background? :) – Zavael Feb 10 '12 at 07:36
  • 1
    @Zavael: The cast itself might be dangerous because it does not really check the types (if you use `(object)`). But in your case you can cast to `InterfaceBase` instead (this will fail if you try to use it with `StringBuilder` and ensures some safety). To write the code with gray background, enclose it in backticks ( ` ). For more formatting check the link "Help" below "Add Comment" button. – Sergey Kudriavtsev Feb 10 '12 at 08:17
2

To answer one part of your question:

return new Concrete1() as T; // type T cannot be used with the as 

Type T cannot be used with as because T is not known to be a reference type. You can constrain the class to be a reference type with the where T : class [, ...] constraint. This will allow you to use the as operator, assuming, of course, that you don't need to be able to use this method with value types.

EDIT

Having said that, I prefer rsenna's answer. Since you know at compile time that the direct cast will work, it makes more sense to use a direct cast than as. I also agree with his recommendation that you investigate "real" IoC containers, but I would add that a strong understanding of generics will be very useful to you as you learn about them. Since you say that you are a beginner in generics, an exercise like this one is probably a good idea, because it will help you learn about generics, and give you a better appreciation for the value that IoC containers can add.

EDIT 2

I see another problem: You constrain T to be InterfaceBase, and then you cast Concrete1 to T. Concrete1 is not known to derive from T! Which is of course why you use the as cast. So the answer is, add the class constraint and you should be fine.

EDIT 3

As rsenna points out, you can also get a runtime type check with an upcast and downcast:

return (T)(object)new Concrete1();

or

return (T)(InterfaceBase)new Concrete1();

I wonder how this compares in terms of efficiency with

return new Concrete1() as T;

I'll check later today if I find some time to do so.

phoog
  • 42,068
  • 6
  • 79
  • 117
  • 1
    Regarding your new edits: If we add the `class` constraint, we cannot use `Factory.Get()` anymore. – rsenna Feb 09 '12 at 16:07
  • 2
    @rsenna If you think it's not possible because `Interface1` is not a class then you are mistaken. As a generic parameter constraint, "class" means "any reference type", just as "struct" means "any value type". Confusing, but true. – phoog Feb 09 '12 at 16:17
  • Sorry, I forgot `class` only requires the type parameter to be a reference type. – rsenna Feb 09 '12 at 16:21
  • @rsenna no need to apologize :) this entire exchange has been somewhat confusing. – phoog Feb 09 '12 at 16:22
  • +1 uaaaaa great thanks, i tought the class means that it needs to be a class! – Zavael Feb 10 '12 at 07:13
1

Doing something like this

public static class Factory {

public static T Get<T>() where T: InterfaceBase{

  return (T) new Concrete1();
}

Cannot be typed safely. You cannot guarantee that the method will be invoked with T == Concrete1. T can be ANY subtype of InterfaceBase, Concrete1 is just one of those subtypes and not necessarily the same T, therefore the compiler won't allow you to cast to T, the same way it won't allow you to cast to string or any other unrelated type.

Activator.CreateInstance() is one way of handling that: CreateInstance does guarantee that the built instance is of type T, which is the expected output value of the method.

dds
  • 83
  • 1
  • 9
  • 1
    But the OP wants to be able to call `Get()` in which case `Activator.CreateInstance()` will fail. He needs a way of mapping `Interface1` to `Concrete1`. – phoog Feb 09 '12 at 16:21
0

If you know that T is a subtype of InterfaceBase (where T : InterfaceBase) then you can make the return type of the Get method be InterfaceBase:

public static InterfaceBase Get<T>() where T : InterfaceBase
{        
    return new Concrete1();
}

This method can be invoked using a sub-interface of InterfaceBase as T:

InterfaceBase ib = Factory.Get<Interface1>();

If you pass anything but a sub-interface of InterfaceBase as T the compiler will complain.

I think this is a better approach but as @phoog points it only works with concrete classes for the T type parameter:

public static T Get<T>() where T : InterfaceBase
{
    Type type = typeof(T);
    if (t.IsAbstract || t.IsInterface)
{
        throw new ArgumentException(@"Only non-abstract classes supported as T type parameter.");
}
    return Activator.CreateInstance<T>();
}
Diego
  • 1,531
  • 1
  • 15
  • 27
  • I just left the first line of the code posted in the question – Diego Feb 09 '12 at 15:27
  • This won't work either, since Concrete1 is not known to derive from T. They both derive from InterfaceBase, but that's not enough. – phoog Feb 09 '12 at 15:40
  • Yes, this work, I'm trying it on a foo project while I'm answering. – Diego Feb 09 '12 at 15:49
  • @Diego "this won't work either" was in reference to `return new Concrete1();` -- does that work? I don't think it should. Can you post a complete example that shows that it does? Of course, Activator.CreateInstance compiles, but the OP wants to pass an interface and then return a type that implements the interface. In that case, you'll get an exception at run time. – phoog Feb 09 '12 at 16:08
  • 1
    @phoog Regarding the Activator.CreateInstance you are right, if you pass an interface or if you pass an abstract class it will throw an exception at runtime. You can only use non-abstract classes for T and a check needs to be added to enforce this. I'm updating the code, but my feeling is that both solutions are fragile. – Diego Feb 09 '12 at 16:13