简体   繁体   中英

How can I write a function that returns any object derived from a base class in C#?

I have a base class, say A.

From that base class I have derived a bunch of other classes, say A1, A2, A3 ...

I have a third class, say B. One of the methods it has needs to be able to return any class as long as it is derived from A.

I tried the following:

public T getObject<T>() where T : A
{
   return (new A1());
}

However this returns an error that says cannot convert type A1 to T.

Can this generic return be implemented?

If you know your class will inherit from A, but don't need to know the exact type then you don't need generics:

public A getObject()
{
    return new A1();
}

If your example had compiled that would mean you could call

A2 result = getObject<A2>();

But your implementation of getObject always returns a new A1() . Since A1 doesn't inherit A2 this is would be wrong.

It sounds like you might want to return different types depending on the result of some mystery logic. This is fine, there's still no need for generics:

public A getObject()
{
    switch(MysteryLogic())
    {
        case MysteryLogicResult.One:
            return new A1();
        case MysteryLogicResult.Two:
            return new A2();
        case MysteryLogicResult.Three:
            return new A3();
    }
}

It sounds like you need to combine this with a factory:

public interface IAFactory
{
    A Build();
}

Then you can change your generic method:

public A getObject<T>() where T : new, IAFactory
{
    return getObject(new T()
}

public A getObject<T>(T factory) where T : IAFactory
{
    return factory.Build();
} 

You'll need to change your implementation of A1, A2:

public class A1 : A, IAFactory
{
   public A1 Build(){
      //Logic for constructing A1
   }
}

Or create a dedicated class:

public class A1Factory : IAFactory
{
   public A1 Build(){
      //Logic for constructing A1
   }
}

Then you can call your method:

public class Test
{
  public void CallBMethod()
  {
     //option 1
     A option1 = new B().getObject<A1>();

     //option 2
     A option2 = new B().getObject<A1Factory>();

    //Or skip the B.getObject method and access factory directly:
    A a1 = new A1Factory().Build();
    A a2 = new A2Factory().Build();
  }
}

TL;DR: You have to return T because that's the return type you declared for your method.

If you return A1, it looks like it should work as you stated. But you forgot that you can also call the method by passing in A2, in which case return A1 is not valid:

var myObject = getObject<A1>(); // this would have worked
var myObject = getObject<A2>(); // this can not work

This will fail, because A1 is not assignable to the type A2. Because the 2nd case cannot work, the compiler will not allow you to do that.

So then the correct way would be to return new T instead:

public T getObject<T>() where T : A
{
   return (new T());
}

var myObject = getObject<A1>(); // works
var myObject = getObject<A2>(); // also works

You can use factory pattern to get desired results:

public A GetObject (string type) {
     A aObj ;
     if (type == "A1") {
        aobj = new A1 () ;
     else if (type == "A2") {
         aobj = new A2 () ;
     }
     return aObj ;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM