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 の意味は返される必要があります、2Questionnaireの意味) と名前を取るメソッドを書きたいと思います。

このメソッドは、次の 2 つのクラスのいずれかのインスタンスを返す必要があります。

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

15

このようなものはどうですか:

public interface IHasOutput
{
    void Output();
}

public class Articles : IHasOutput

public class Questionnaire : IHasOutput

その後:

public static IHasOutput Choose...

もちろん、インターフェースの名前は、 以外の任意の名前にすることができますIHasOutput。これがインターフェースの目的です。共通のインターフェースを共有する 2 つの異なる具体的な実装。これを呼び出すと、次のことができます。

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

どの具体的な実装が返されるかは問題ではありません。共通のインターフェースを実装していることはご存知でしょう。

于 2013-07-05T14:23:07.250 に答える
7

ここで提供される回答は素晴らしいですが、私が気に入らないことの1つはx、作成するタイプを選択するパラメーターです。これにより、後で頭が痛くなる可能性のあるマジックナンバーが使用されます。

ここでジェネリックス、つまり make method を利用できます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 factory patternについてもっと読むことに興味があるかもしれません。

于 2013-07-05T14:50:21.397 に答える
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)
    {
    }
}

アップデート

この問題を解決するもう 1 つの非常に簡単な方法は、オーバーライドすることです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 に答える