返回共享方法的两种不同类型的可能对象之一

时间:2013-07-05 14:20:07

标签: c# .net oop factory-pattern duck-typing

我有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);
    }
}

我想写一个方法,它取一个整数(1表示应该返回Articles,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()

6 个答案:

答案 0 :(得分:27)

为什么没有定义Output的{​​{3}}。然后返回基地。

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

ArticlesQuestionaire都应该继承此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;
    }
}

您也可以通过base class来实现这一目标。

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();}

}

答案 1 :(得分: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();

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

答案 2 :(得分:7)

这里提供的答案很棒,但有一点我不喜欢参数x,它选择应该创建什么类型。这创造了magic number的使用,即使对你来说也可能会头疼。

您可以在此处利用泛型,即制作方法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");

Demo

修改

正如@ 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中更改类创建的逻辑。)

P.S。您可能有兴趣阅读有关factory pattern的更多信息。

答案 3 :(得分:5)

除非他们共享相同的基类或界面,否则您很可能会遇到objectdynamic

答案 4 :(得分: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

答案 5 :(得分:0)

您有3个选择:

1)使调查问卷和文章继承自同一个基类,并使该基类的类型成为您方法的返回类型。

2)使你的返回类型为Object。

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