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

我有2个class:

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

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

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

ArticlesQuestionaire都应该inheritance此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 ,并将原始或转发调用inheritance到相应的方法:

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

这样的事情怎么样:

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

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

这里提供的答案很棒,但我不喜欢的一件事是参数x ,它选择应该创建什么类型。 这创造了魔法数字的使用,即使对你来说也可能变得头疼。

你可以在这里利用generics,即make方法Choose

 public static T Choose(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("name"); Questionnaire questionnaire = ClassWithChooseMethod.Choose("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(name); case ArticleType.Questionnaire: return ClassWithChooseMethod.Choose(name); default: return default(BaseClass); } } 

和用法:

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

这使您可以保持代码更清洁,并有助于将来的维护(例如,您可以在Choose更改类创建的逻辑)。

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

除非它们共享相同的基类或接口,否则您几乎无论是object还是dynamic

解决此问题最灵活的方法是编写一个接口以及一个实现它的抽象基类。 这样,如果基类在非常特殊的情况下不满足您的需要,或者类已经从另一个类派生,您可以自由地从基类派生类或直接实现接口。 也使方法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) { } } 

UPDATE

解决问题的另一种非常简单的方法是覆盖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

你有3个选择:

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

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

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