18

我有 2 节课:

public class Articles
{
    private string name;

    public Articles(string name)
    {
        this.name = name;
    }

    public void Output()
    {
        Console.WriteLine("The class is: " + this.GetType());
        Console.WriteLine("The name is: " + name);
    }
}

public class Questionnaire 
{
    private string name;

    public Questionnaire(string name)
    {
        this.name = name;
    }

    public void Output()
    {
        Console.WriteLine("The class is: " + this.GetType());
        Console.WriteLine("The name is: " + name);
    }
}

我想写一个方法,它需要一个整数(Articles应该返回 1 的含义,2 的含义Questionnaire)和一个名称。

此方法必须返回这两个类之一的实例:

public [What type??] Choose(int x, string name)
    {
        if (x == 1)
        {
           Articles art = new Articles(name);
           return art;
        }
        if (x == 2)
        {
            Questionnaire ques = new Questionnaire(name);
            return ques;
        }
    }

我应该使用什么返回类型,所以我可以调用Output()结果?

4

6 回答 6

27

为什么没有一个已经定义的基类。Output然后返回基地。

public abstract class BaseType {
    public abstract void Output();
}

两者都Articles应该Questionaire继承 this BaseType

public class Articles : BaseType {
  // Output method here
}

public class Questionaire : BaseType {
 // Output method here
}

然后你可以这样做:

public static BaseType Choose(int x, string name) 
{
    if (x == 1)
    {
       Articles art = new Articles(name);
       return art;
    }
    if (x == 2)
    {
        Questionnaire ques = new Questionnaire(name);
        return ques;
    }
}

您也可以通过interface.

public interface IInterface {
    void Output();
}

public class Articles : IInterface {
  // Output method here
}

public class Questionaire : IInterface {
 // Output method here
}

然后,您必须修改 Choose 方法以返回IInterface而不是BaseType. 无论您选择哪个都取决于您。

注意:即使您无法更改原始类,您仍然可以dynamic通过提供实现接口并继承原始或将调用转发到相应方法的包装类来使用这些方法:

public class ArticlesProxy : Articles, IInterface 
{
  public ArticlesProxy(string name) : base(name){}

}

public class QuestionaireProxy : Questionaire, IInterface {
  Questionaire inner;
  public QuestionaireProxy(string name) {  inner = new Questionaire(name); }

  public void Output() { inner.Output();}

}
于 2013-07-05T14:22:16.620 回答
15

像这样的东西怎么样:

public interface IHasOutput
{
    void Output();
}

public class Articles : IHasOutput

public class Questionnaire : IHasOutput

接着:

public static IHasOutput Choose...

你当然可以给你的界面起任何你喜欢的名字,除了IHasOutput,我只是不知道该怎么称呼它。这就是接口的用途。共享一个公共接口的两个不同的具体实现。现在,当您调用它时,您可以这样做:

var entity = MyClass.Choose(1, "MyName");
entity.Output();

并且返回什么具体实现并不重要。你知道它实现了一个通用接口。

于 2013-07-05T14:23:07.250 回答
7

这里提供的答案很好,但我不喜欢的一件事是x选择应该创建什么类型的参数。这创造了幻数的使用,即使以后你也可能会感到头疼。

您可以在这里利用泛型,即 make 方法Choose

public static T Choose<T>(string name)
        // type constraint to ensure hierarchy.
        where T : BaseClass // BaseClass have common functionality of both class.
    {
        // Unfortunately you can't create instance with generic and pass arguments
        // to ctor. So you have to use Activator here.
        return (T)Activator.CreateInstance(typeof(T), new[] { name });
    }

用法:

Articles article = ClassWithChooseMethod.Choose<Articles>("name");
Questionnaire questionnaire = ClassWithChooseMethod.Choose<Questionnaire>("name2");

演示

编辑

正如@OlivierJacot-Descombes 在评论中提到的x选择类型可能是用户输入。在这种情况下,您可以enum使用相应的值创建:

enum ArticleType {
    Articles = 1,
    Questionnaire = 2
}

并且有过载Choose

public static BaseClass Choose(ArticleType type, string name) {
    switch (type) {
        case ArticleType.Articles:
            return ClassWithChooseMethod.Choose<Articles>(name);
        case ArticleType.Questionnaire:
            return ClassWithChooseMethod.Choose<Questionnaire>(name);
        default:
            return default(BaseClass);
    }
}

和用法:

var obj = ClassWithChooseMethod.Choose((ArticleType)userInput, "some name");

这使您有可能使您的代码更干净,并且对将来的维护有用(例如,您可以在 中更改类创建的逻辑Choose)。

PS您可能有兴趣阅读有关工厂模式的更多信息。

于 2013-07-05T14:50:21.397 回答
5

除非它们共享相同的基类或接口,否则您几乎会被困在其中一个objectdynamic.

于 2013-07-05T14:22:15.670 回答
1

解决这个问题最灵活的方法是编写一个接口以及实现它的抽象基类。这样,如果基类在非常特殊的情况下不能满足您的需求,或者如果一个类已经从另一个类派生,那么您可以自由地从基类派生一个类或直接实现接口。还要使方法Output虚拟;这使您可以在必要时覆盖它。也使name受保护;这使您可以在派生类中使用它

public interface IHasOutput
{
    void Output();
}

public abstract class OutputBase : IHasOutput
{
    protected string _name;

    public OutputBase(string name)
    {
        _name = name;
    }

    #region IHasOutput Members

    public virtual void Output()
    {
        Console.WriteLine("The class is: " + this.GetType());
        Console.WriteLine("The name is: " + _name);
    }

    #endregion

    public static IHasOutput Choose(int x, string name)
    {
        switch (x) {
            case 1:
                return new Articles(name);
            case 2:
                return new Questionnaire(name);
            default:
                return null;
        }
    }
}

public class Articles : OutputBase
{
    public Articles(string name)
        : base(name)
    {
    }
}

public class Questionnaire : OutputBase
{
    public Questionnaire(string name)
        : base(name)
    {
    }
}

更新

解决问题的另一种非常简单的方法是覆盖ToString

public override string ToString()
{
    return String.Format("The class is: {0}\r\nThe name is: {1}", 
                         this.GetType(), _name);
}

你可以这样称呼它:

object obj = Factory.Choose(1, "Test");
Console.WriteLine(obj);

不需要接口,也不需要基类!好吧,准确地说,基类object当然是。

于 2013-07-05T14:49:09.053 回答
0

您有 3 个选择:

1) 使 Questionnaire 和 Article 继承自同一个基类,并使该基类的类型成为您方法的返回类型。

2)使您的返回类型对象。

3)使您的返回类型动态。

于 2013-07-05T14:24:04.407 回答