2017-12-25 12 views
0

私は検索して検索しましたが、正直言って、何を探すべきか分かりません。 私はこの抽象基本クラスをItemとしています。基本クラスにメソッドSplit(double amount)を使用する必要がある多くのサブクラスがあります。私は、そのメソッドが、サブクラスがそれをどのように呼び出すのか何とか知り、そのタイプの新しいオブジェクトをインスタンス化できるようにしたい。 これは私の奇妙なゲームですが、私はこの具体的な方法が2つにアイテムのスタックを分割する必要があります...メソッドを呼び出すサブクラスに応じて新しいサブクラスオブジェクトをインスタンス化する基本クラスメソッドを作成する

!!!サブクラスがアイテムタイプとそのアイテムタイプの数量を表現するにもかかわらず、基本クラスはItemと呼ばれます。

これは私がこれまで持っているものです:

public abstract class Item 
{ 
    public double Quantity { get; private set; } 

    public Item() 
    { 
     Quantity = 1; 
    } 

    public Item(double quantity) 
    { 
     Quantity = quantity; 
    } 

    public bool Merge(Item item) 
    { 
     if (item.GetType() == GetType()) 
     { 
      Quantity += item.Quantity; 
      return true; 
     } 
     return false; 
    } 

    public Item Split(double amount, Type häst) 
    { 
     Quantity -= amount; 
     //Here I'd like to do something like "new the-type-that-called-this-method()" 
    } 
} 

は、私は、基本クラスで抽象メソッドを使用して、すべてのサブクラスでそれをオーバーライドすることができることを知っているが、その後、私は再する必要があると思います - 基本的にまったく同じコードを書いて何度か分からない...もっと良い方法が見つからない場合、これが私のやり方だと思います...

ご存知の場合は他の提案や、これを整理する私のやり方が馬鹿だと思うならば、私に言い聞かせないでください。私はこのようなことは一度もしていませんし、単純なことを複雑にする傾向があり、 eason ...

もっと良い説明??? たとえば、スタックが200 IronOreIronOre : Itemとしましょう。このインスタンスはmyIronPileに格納されています)の場合、70 IronOreを取得して、新しい「スタック」に入れたいと思います。したがって、myOtherIronPile = myIronPile.Split(70)に電話したいと思います。これにより、myIronPileから70が削除され、新しいIronOreインスタンスに格納され、myOtherIronPileに格納されます。

どのようなヘルプも大いに魅力的です!ありがとう! :D

+0

私はたぶんキーワードが助けになると理解したように –

答えて

0

をあなたが(System.Reflection名前空間から)Activator.CreateInstanceを使用することができます与えられた型のオブジェクトを作成するには:

public Item Split(double amount) 
{ 
    Quantity -= amount; 
    return Activator.CreateInstance(GetType(), new object[] { amount }) as Item; 
} 

GetTypeがほとんどで、オブジェクトの「本当の」型を返す仮想呼び出しです派生した形。


良い方法はあなたの代わりに、基本クラスItemの派生型で動作するよう汎用的な方法を作るために次のようになります。

public static T Split<T>(T item, double amount) where T: Item 
{ 
    item.Quantity -= amount; 
    return Activator.CreateInstance(typeof(T), new object[] { amount }) as T; 
} 

次に、あなたがそのようにそれを使用します。

var myIronPile = new IronOre(200); 
var myOtherIronPile = Item.Split(myIronPile, 70); 

Item変数でこれらの操作を使用すると注意してください。これはうまくいかないため、

List<Item> items = new List<Item>(); 
items.Add(new IronOre(200)); 
var myOtherPile = Item.Split(items[0], 70); // call to Item.Split<Item>(...) ! 

Activator.CreateInstanceは、インスタンス化するタイプが抽象であるため、例外をスローします。

List<Item> items = new List<Item>(); 
items.Add(new IronOre(200)); 

var myOtherPile = Item.Split(items[0] as IronOre, 70); // works 

IronOre item = items[0] as IronOre; 
myOtherPile = Item.Split(item, 70); // also works 

他のソリューションだけで、おそらくunnoticableパフォーマンスの低下を招きGetTypeとランタイム型を(typeofは、コンパイル時構築物である)を使用することです:

public static T Split<T>(T item, double amount) where T: Item 
{ 
    item.Quantity -= amount; 
    return Activator.CreateInstance(item.GetType(), new object[] { amount }) as T; 
} 

一つの問題は、しかし、持続:

List<Item> items = new List<Item>(); 
items.Add(new IronOre(200)); 

var myOtherPile = Item.Split(items[0], 70); // myOtherPile is of type Item 

IronOre myOtherPile = Item.Split(items[0], 70); // error, cast needed 
+0

恐ろしい!私はそれを試してみよう! –

0

最初に、Splitがあなたのメソッドの悪い名前のようです。あなたのメソッドの名前を見ると、それが何をしているのかということに気づく最初のものは、アイテムを与えられたとき、それは2つの新しいものに分割されます。しかし、それはそうではありません。実際には、指定された項目を変更し、その一部を新しい項目に抽出します。

ええと... Extractいい名前ですか?

public Item Extract(double amount) 

この方法が表示されたら、最初のインプレッションは実際に実際に行ったものと一致します。

できるだけ強く型付けするためにジェネリックを使用することもできます。静的メソッドを作成すると、型推論を利用して助けてもらうことができます(名前を少し変更して可能であれば拡張メソッドの変更を避けようとしていますが、これを拡張メソッドとして実装することも考えられます。

ランタイムタイプはitemで、typeof(T)ではないことに注意してください。これは、単に型推論がItemに解決される可能性があり、抽象クラスを直接インスタンス化できないため、ランタイムエラーが発生する可能性が高いという事実によります。

+0

この場合、 'Quantity'は専用のセッターを持っているので、突然変異拡張メソッドは不可能です。 –

+0

@JakubDąbek真、その詳細を逃した、拡張メソッドは、私が私の答えで述べたように良いですオプションはありません。 – InBetween

0

あなたはItemクラスは一種の自己型のように、それが表す具体的なItemタイプのため、型パラメータを取る作ることができます。また、これはMerge方法に役立ちますし、あなたが作成を容易にするために、工場Createメソッドを持つことができます。

public abstract class Item<T> where T : Item<T>, new() { 
    public double Quantity { get; private set; } 
    public static T Create(double quantity) { 
    return new T { Quantity = quantity }; 
    } 
    public void Merge(T item) { 
    Quantity += item.Quantity; 
    } 
    public T Split(double amount) { 
    Quantity -= amount; 
    return Create(amount); 
    } 
} 

サブクラス型パラメータとして自分自身を提供しなければならないし、また、(暗黙の1が細かいパラメータなしのコンストラクタを持っている必要があります):

public class IronOre : Item<IronOre> { } 

また、あなたが、この自己typeパラメータのみが慣例であることに注意して、サブクラスの振る舞いを確認します一つです。あなたの例に基づいて

使用例:このパターンとその落とし穴hereについて

var myIronPile = IronOre.Create(200); 
... 
var myOtherIronPile = myIronPile.Split(70); 

詳細情報。

関連する問題