views:

130

answers:

4

Is there a way to do something like this in c#? Consider the following example and assume that Child1, Child2, Child3 are all children of Parent -

class Class1
{

  SomeObject< Parent > mSomeObject; 

  Class1()
  {

     if (condition1)

          mSomeObject = new SomeObject<Child1>();

     else if (condition2) 

          mSomeObject = new SomeObject<Child2>(); 

     else if (condition3) 

          mSomeObject = new SomeObject<Child3>();

  }

}

The idea is that that Class1 would have SomeObject as a member, but it is uncertain until runtime what generic form of SomeObject it should take. Any help would be appreciated. Thanks!

+1  A: 

I've never tried it, but I believe you are correct. In general if

public class Child : Parent

then any call for Parent will be satisfied by Child.

For instance:

public class Animal { //stuff }
public class Cat : Animal { // overridden stuff }

List<Animal> pets = new List<Animal>;
pets.Add(new Cat());

would work.

If you're looking for behavior, you may want to be working with Interfaces instead of class inheritance, but it would work the same way.

No, the problem you may have with your approach is that your SomeObject<Parent> will only have access to those members in your Parent class. To use anything in the appropriate Child class, you'll need to cast back to the appropriate Child.

AllenG
Ah, but List<Animal> pets = new List<Cat>(); does *not* work. Do you see why? Because you can then say pets.Add(new Dog()) and now you've put a dog into a list of cats!
Eric Lippert
I prefer `List<Animal> henhouse = new List<Chicken>();` so you can (try to) let a fox into the henhouse.
Anthony Pegram
+1  A: 

You can do this in C# 4.0 with an interface or a delegate but not with a class. Consider changing the property to an interface type and read more about generics covariance and contravariance in C# 4.0 (for example here: http://msdn.microsoft.com/en-us/library/ee207183.aspx )

Stilgar
+2  A: 

You could do this. Notice the use of the out keyword here to make the type covariant.

public interface ISomeObject<out T>
{
}

However, this will somewhat restrict what you can do with T in the interface. Specifically you can only declare members where T is in the output position. In other words, it cannot be accepted as a parameter to a function.

Brian Gideon
Addendum (as mentioned elsewhere): In C# 4
Marc Bollinger
you can't do this with a class
Stilgar
@Stilgar: Nice catch. I fixed it.
Brian Gideon
Thanks for the tip. Unfortunately I'm stuck in the dark ages of .net 2.0.
Blake
+2  A: 

You should use interface based inheritance. This will allow child1, child2, and child3 to be polymorphic and take on the characteristics of the parent without the need for such guard logic. With the IF tests gone your code will be more readable and easier to modify later.

Here's and example I just wrote with LINQPad to show this in action.

public interface ICar
{
    bool IsAutomatic();
}

public class Silverado : ICar
{
    public bool IsAutomatic()
    {
        return true;
    }
}

public class Semi : ICar
{
    public bool IsAutomatic()
    {
        return false;
    }
}

void Main()
{
    ICar car = new Silverado();
    bool isAuto = car.IsAutomatic();
    isAuto.Dump();

    car = new Semi();
    isAuto = car.IsAutomatic();
    isAuto.Dump();
}

OUTPUT:

True
False

I prefer interface based inheritance as opposed to abstract classes as described by AllenG. Reasons such as multiple inheritance - a class can implement many interface but only inherit from 1 class.

Hope this helps...

pjacobs
Thanks, this works great. I made SomeObject<T> : IObjectInterface. In Class1, I changed the type of mSomeObject to IObjectInterface. Finally, in the constructor, I instantiated the appropriate generic version and all the types worked out!
Blake