2012-07-15 18 views
5

मैं किसी भी आरपीजी की तरह वस्तुओं की एक साधारण "सूची" बनाने की कोशिश कर रहा हूं। मेरे पास बहुत मूल कक्षाएं हैं, जिनमें गुण हैं।सी # सूची <baseClass> - विरासत में कक्षाओं को भी स्टोर करने का कोई तरीका भी?

वैसे भी, मेरे पास item का बेस क्लास है और उसमें से विरासत weapon है। item में गुण (नाम, मूल्य, वजन, "महत्वपूर्ण वस्तु") हैं जिनका उपयोग weapon के भीतर भी किया जाता है, लेकिन weapon में अतिरिक्त गुण (आक्रमण, रक्षा, गति, सौहार्द) हैं।

मैं निम्नलिखित कोड मिल गया है (खेद है पठनीयता भयानक है):

static void Main(string[] args) 
{ 
    List<item> inventory = new List<item>(); 
    inventory.Add(new weapon("Souleater", 4000, 25.50f, false, 75, 30, 1.25f, 2)); 
          //item---------------------------> weapon---------> 

    Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack: {4}\nDefense: {5}\nSpeed: {6}\nHandedness: {7}", 
      inventory[0].Name, inventory[0].BValue, inventory[0].Weight, inventory[0].Discard, 
      inventory[0].Atk, inventory[0].Def, inventory[0].Speed, inventory[0].Hands); 

    Console.ReadLine(); 
} 

मूल रूप से, मैं क्या सूची के लिए एक नया weapon जोड़ने करना कोशिश कर रहा हूँ, लेकिन सूची एक है List<item> प्रकार। मैं उम्मीद कर रहा था कि, इसकी विरासत के कारण, इसे स्वीकार किया जाएगा। यह था, लेकिन गुण weapon के लिए विशिष्ट पहुँचा जा करने में सक्षम नहीं हैं:

('shopSystem.item' 'Atk' के लिए एक परिभाषा और कोई विस्तार विधि 'Atk' प्रकार का एक पहला तर्क को स्वीकार करने में नहीं है 'shopSystem.item' पाया जा सकता है)

तो, क्या मैं यहां जो इरादा कर रहा था उसे हासिल करने का कोई तरीका है? एक "इन्वेंट्री" है जो item ऑब्जेक्ट्स, साथ ही साथ weapon, armour, accessory आदि ऑब्जेक्ट्स स्टोर कर सकती है, जो item से प्राप्त होती हैं? यह उल्लेख करने योग्य भी है कि यदि मैं निम्नलिखित घोषित करता हूं तो मैं सभी वांछित गुणों तक पहुंच सकता हूं:

weapon Foo = new weapon("Sword", 200, 20.00f, false, 30, 20, 1.10f, 1); 

पढ़ने के लिए बहुत धन्यवाद।

यहाँ, item और weapon वर्गों है कि अगर किसी को दिलचस्पी है:

class item 
{ 
    #region Region: Item Attributes 
    protected string name = ""; 
    protected int baseValue = 0; 
    protected float weight = 0.00f; 
    protected bool noDiscard = false; 
    #endregion 

    public item(string n, int v, float w, bool nd){ 
     name = n; baseValue = v; weight = w; noDiscard = nd;} 

    public string Name{ 
     get{return name;} 
     set{if(value != ""){ 
      name = value;} 
     }//end set 
    } 

    public int BValue{ 
     get{return baseValue;} 
    } 

    public float Weight{ 
     get{return weight;} 
    } 

    public bool Discard{ 
     get{return noDiscard;} 
    } 
} 

class weapon : item 
{ 
    #region Region: Weapon Attributes 
    private int atk = 0; 
    private int def = 0; 
    private float speed = 0.00f; 
    private byte hands = 0; 
    #endregion 

    public weapon(string n, int v, float w, bool nd, int a, int d, float s, byte h) : base(n, v, w, nd){ 
     atk = a; def =d; speed = s; hands = h;} 

    public int Atk{ 
     get{return atk;} 
    } 

    public int Def{ 
     get{return def;} 
    } 

    public float Speed{ 
     get{return speed;} 
    } 

    public byte Hands{ 
     get{return hands;} 
    } 

} 

उत्तर

8

गुण विरासत में मिला वर्ग के लिए विशिष्ट उपयोग करने के लिए, आप उचित ठोस वर्ग प्रकार के कास्ट करने के लिए की जरूरत है। तो, inventory[0] तक पहुंचने के बजाय, आपको (weapon)inventory[0] तक पहुंचने की आवश्यकता है।

इसके अलावा, अगर आप सामान्य कार्यों प्रदर्शन कर रहे हैं अलग अलग तरह के कंसोल के लिए मूल्यों को प्रवेश करने के रूप में अंतर्निहित वर्ग के आधार पर कार्यान्वयन, होगा कि (जो मैं परीक्षण के लिए किया गया था एहसास है, लेकिन एक अच्छा उदाहरण वैसे भी है), तो आप चाहिए बेस क्लास में एक अतिव्यापी विधि लागू करें।

उदाहरण के लिए, आधार वर्ग में:

public virtual void LogProperties() 
{ 
Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack", 
      this.Name, this.BValue, this.Weight, this.Discard); 
} 

और हथियार कक्षा में:

public override void LogProperties() 
{ 
Console.Write("Name: {0}\nValue: {1}\nWeight: {2}\nDiscardable: {3}\nAttack: {4}\nDefense: {5}\nSpeed: {6}\nHandedness: {7}", 
      this.Name, this.BValue, this.Weight, this.Discard, 
      this.Atk, this.Def, this.Speed, this.Hands); 
} 

फिर, इस का उपयोग करने की:

inventory[0].LogProperties(); 
+1

अगर मैं का उपयोग '(हथियार) सूची [0] .Atk', मैं अभी भी एक ही त्रुटि संदेश मिलता है। – Deadrust

+1

का उपयोग कर ((हथियार) सूची [0])। Atk बजाय –

+0

ऐ प्रयास करें, कि एक काम करता है! धन्यवाद! – Deadrust

0

आप एक आम लागू कर सकते हैं इंटरफेस (उदाहरण के लिए IItem) जिसके माध्यम से आप वस्तुओं के गुण पूछते हैं, या एक सामान्य बेस क्लास, प्रो बेकार एक अमूर्त एक, और List के प्रकार T के रूप में उपयोग करें। यदि बाद वाले विकल्प का उपयोग करते हैं, तो उचित श्रेणी के कार्यों को ओवरराइड करना सुनिश्चित करें, जहां बहुरूपता शेष करेगी।

0

उन गुणों तक पहुंचने के लिए आपको मान को weapon पर वापस डालना होगा, यानी ((weapon)inventory[0]).Atk)। कोष्ठक नोट करें: कास्टिंग क्षेत्र की पहुंच से कम सटीकता है, इसलिए आप (weapon)inventory[0].Atk नहीं लिख सकते हैं; जिसे (weapon)(inventory[0].Atk) के रूप में व्याख्या किया जाएगा।

आपको विभिन्न वर्गों के बीच अंतर करने की भी आवश्यकता होगी (उदाहरण के लिए, आपके पास शायद armor कक्षा भी हो)। ऐसा करने के लिए सबसे सरल आधार वर्ग item पर एक अतिरिक्त मूल्य जोड़ना होगा जो आपको वस्तु का वास्तविक प्रकार बताता है, और फिर तदनुसार कास्ट करता है।

हालांकि, विरासत का उपयोग करने के लिए एक बेहतर दृष्टिकोण होगा - item उन तरीकों की एक श्रृंखला को परिभाषित करें जो सभी सामान्य सामग्री को संभाल सकते हैं - उदा। आइटम के बारे में जानकारी प्रदर्शित करना - और फिर विरासत कक्षाओं में इसे ओवरराइड करें। यही है, उस डेटा को सीधे एक्सेस करने के बजाय, आप एक विधि को बुलाते हैं जो इसे समझदार तरीके से जोड़ता है। अपने विशेष उदाहरण के लिए, यदि आप एक स्ट्रिंग के लिए आइटम परिवर्तित कर रहे हैं, तो यह शायद मतलब होगा toString() ओवरराइड करने के लिए - इसका मतलब है कि आप ऐसा करने में सक्षम हो जाएगा:

Console.Write(inventory[0]); 

और वह यह है कि इस पर निर्भर एक वस्तु या हथियार, उपयुक्त वर्ग के ToString() को कॉल किया जाएगा (आमतौर पर, आपको विधि को स्वयं ही कॉल करना होगा, लेकिन ToString() विशेष है क्योंकि सभी ऑब्जेक्ट्स में वह विधि है, इसलिए कंसोल। यह इसके लिए करता है आप)।

अपने हथियार वर्ग में, आप तो इस तरह ToString विधि लागू होगी:

public string ToString() { 
    //Put all of the data in here, like in your example 
    return String.Format("Name: {0}\n...", name, ...); 
} 
+0

मुझे यह ToString विधि पसंद है। हालांकि, अगर मैं गणितीय सूत्र में उपयोग करने के लिए उन गुणों में से केवल एक (कहां, एटीके संपत्ति) का उपयोग करना चाहता हूं तो क्या होगा? क्या यह वही तरीका है जो इसे रखने के लिए सबसे अच्छा होगा। ((हथियार) सूची [0])। अटका '? मैं जिस स्ट्रिंग को बना रहा था वह सिर्फ यह जांचने के लिए था कि मूल्य सही तरीके से संग्रहित हो रहे थे। – Deadrust

+0

@Deadrust: हाँ, आप ऐसा कर सकते हैं, और आप भी कर सकते हैं 'हथियार usedWeapon = (हथियार) सूची [0];' और फिर अतिरिक्त कास्टिंग बिना usedWeapon पर सभी गुण का उपयोग। आप शायद पहले भी ऐसा करना चाहते हैं, उदा। वर्तमान में सुसज्जित खिलाड़ी को 'प्लेयर' या 'इकाई' वर्ग पर संग्रहीत करना, लेकिन आरपीजी इसके दृष्टिकोण पर भिन्न होते हैं। –

+0

यह भी ध्यान रखें कि आपके पास इस उदाहरण के लिए ToString का उपयोग करने के लिए * नहीं है। यह आपकी खुद की विधि को परिभाषित करने के लिए उतना ही समझदारी कर सकता है और इसे कॉल कर सकता है - यह सब इस बात पर निर्भर करता है कि आप अपनी कक्षाओं के साथ क्या करना चाहते हैं। –

2

आप एक List<item> का उपयोग कर वस्तुओं और वस्तुओं के उपवर्गों स्टोर करने के लिए सही काम कर रहे हैं!

आप सूची से आइटम निकालने हैं, तो आप सिर्फ अगर यह एक weapon, या item के कुछ अन्य प्रकार है परीक्षण करने के लिए काम का एक सा करना है। यदि सरणी पर स्थिति i पर तत्व एक weapon है

देखने के लिए, as ऑपरेटर, जो null वापस आ जाएगी यदि आइटम निर्दिष्ट प्रकार नहीं है का उपयोग करें (शायद यह कवच या आइटम के कुछ अन्य प्रकार है)। इसे dynamic कास्ट के रूप में जाना जाता है। यह तब भी काम करता है जब आइटम null है।

weapon w = inventory[i] as weapon; // dynamic cast using 'as' operator 
if (w != null) 
{ 
    // work with the weapon 
} 

यह करने के लिए एक और तरीका है एक static डाली करने से पहले is ऑपरेटर का उपयोग प्रकार की जांच करने के लिए है।

if (inventory[i] is weapon) 
{ 
    // static cast (won't fail 'cause we know type matches) 
    weapon w = (weapon)inventory[i];   

    // work with the weapon 
} 
+1

दूसरे परीक्षण के लिए बेहतर विकल्प: 'अगर (सूची [i] हथियार है) '। लेकिन आपका पहला उदाहरण ('as' के साथ) शायद वैसे भी बेहतर है, क्योंकि यह वस्तु को दो बार कास्टिंग से बचाता है। :) –

+0

@DanJ हाँ, धन्यवाद! यह शून्य जांच की आवश्यकता को भी रोकता है। उत्तर तदनुसार समायोजित करें। :) पहला उदाहरण ठीक है –

+0

यही तो मैं जल्द ही की आवश्यकता होगी, जब मैं गतिशील 'inventory' उदाहरण के लिए आइटम जोड़ने के लिए शुरू करते हैं। आपका बहुत बहुत धन्यवाद! – Deadrust

1

यह वह जगह है जहां पॉलिमॉर्फिज्म से निपटने के दौरान लिंक आपका मित्र हो सकता है। कास्टिंग आइटम के साथ समस्या तब होती है जब दिया गया सूचकांक सही प्रकार नहीं होता है। (अर्थात inventory[0] एक weapon नहीं है, (weapon)inventory[0] फेंक देते हैं।)

आप का उपयोग करके अपवाद से बचने कर सकते हैं:

var myWeapon = inventory[0] as weapon; 

हालांकि तो आप #null चेकों हर जगह कर रही होगी।यहाँ से

var weapons = inventory.OfType<weapon>(); 

आप उपयोग कर सकते Where, FirstOrDefault, आदि डेटा आप रुचि रखते हैं या हथियारों से अधिक पुनरावृति को खोजने के लिए: का प्रयोग Linq आप बहुत लचीलापन मिलता है।

+0

+1 यह मेरा दृष्टिकोण होगा, यह सीमित करता है कि आपके कोड को जेनेरिक सूची के बारे में कितना पता होना चाहिए। उदाहरण के लिए, यदि आप एक विधि है कि एक 'सूची ' पर काम करता है लिख सकते हैं और() 'में' inventory.OfType पारित कर सकते हैं। उस विधि को आपकी सूची में अन्य प्रकार के सामानों के अस्तित्व के बारे में चिंता करने की आवश्यकता नहीं है। –

संबंधित मुद्दे