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

Sas*_*sky 18 .net c# oop duck-typing factory-pattern

我有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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我想写一个方法,它取一个整数(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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

Dar*_*ren 27

为什么没有已定义的基类Output.然后返回基地.

public abstract class BaseType {
    public abstract void Output();
}
Run Code Online (Sandbox Code Playgroud)

双方ArticlesQuestionaire应该继承这个BaseType.

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

public class Questionaire : BaseType {
 // Output method here
}
Run Code Online (Sandbox Code Playgroud)

然后你可以这样做:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

你也可以通过一个来实现这个目标interface.

public interface IInterface {
    void Output();
}

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

public class Questionaire : IInterface {
 // Output method here
}
Run Code Online (Sandbox Code Playgroud)

然后,您必须修改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();}

}
Run Code Online (Sandbox Code Playgroud)


Mik*_*oud 15

这样的事情怎么样:

public interface IHasOutput
{
    void Output();
}

public class Articles : IHasOutput

public class Questionnaire : IHasOutput
Run Code Online (Sandbox Code Playgroud)

然后:

public static IHasOutput Choose...
Run Code Online (Sandbox Code Playgroud)

您当然可以将您的界面称为您想要的任何内容,除此之外IHasOutput,我只是不知道该怎么称呼它.这就是接口的用途.共享公共接口的两种不同的具体实现.现在,当你打电话时,你可以这样做:

var entity = MyClass.Choose(1, "MyName");
entity.Output();
Run Code Online (Sandbox Code Playgroud)

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


Ler*_*eri 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 });
    }
Run Code Online (Sandbox Code Playgroud)

用法:

Articles article = ClassWithChooseMethod.Choose<Articles>("name");
Questionnaire questionnaire = ClassWithChooseMethod.Choose<Questionnaire>("name2");
Run Code Online (Sandbox Code Playgroud)

演示

编辑

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

enum ArticleType {
    Articles = 1,
    Questionnaire = 2
}
Run Code Online (Sandbox Code Playgroud)

并有超载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);
    }
}
Run Code Online (Sandbox Code Playgroud)

和用法:

var obj = ClassWithChooseMethod.Choose((ArticleType)userInput, "some name");
Run Code Online (Sandbox Code Playgroud)

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

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


Joh*_*aft 5

除非他们共享相同的基类或接口,否则你几乎都会遇到object或者dynamic.

  • 我并不是说这是好的设计.只有具有所述设计的那些,这些是选项. (3认同)