簡體   English   中英

當派生對象屬性存儲在父類型集合中時,如何訪問它們?

[英]How to access derived objects properties when they are stored in a Parent typed collection?

我從事RPG方面的工作,並且對如何制作世界上所有物品的全球清單感到困惑。 我使用Unity3D。

我的物品可能是武器,貨幣,Macscots和許多衍生類。

所以我有一個基本的Class Item(簡化代碼)

public class Item{
    public int Id {get;set;}
    public Vector2 GeoCoord{get;set;}
    public int OwnerId{get;set;}
    public string State{get;set;}
    public string Name{get;set;}
    public Type Type{get;set;}
}

和子類

public class WeaponModel: Item {

    public Type CloseTargetBehavior{get;set;}
    public Type DistantTargetBehavior{get;set;}
    ...
}

public class MascotModel: Item {

    public MascotCategory Category {get; set;}
    ...
}


public class ConsumableModel: Item {

    public float Price {get; set;}
    ...    
}

出於節省目的和其他原因,我想將這些派生類的所有實例存儲在字典(或其他任何東西)中。 但我讀到它不建議將多類型對象存儲在集合中,因為您只能訪問它們的公共屬性(此處為Item的類屬性)。

那么,擁有所有對象的全局列表並仍然可以訪問其屬性的最佳方法是什么?

編輯1:添加了一些用法詳細信息

該集合的第一個用途是將其用作一些“工廠”的描述:

1)當游戲開始時,我反序列化一個JSON文件來構建這個唯一的集合。 我使用http://www.dustinhorne.com/post/Json-NET-for-Unity-Developers。我使用settings.TypeNameHandling = TypeNameHandling.All; 保留所有類/子類描述的參數。

2)不同的具體GameObject(可以同化為View元素)被用於2Dmap,3Dworld,清單...中,但是每個表示形式都引用子類實例(WeaponModel或MascotModel或...)。 例如:對地圖上某個項目或3D視圖中的項目的更改都會改變模型的狀態。

我想到了“ if(typeOf(WeaponModel))else if ...”,但這是我困惑的起點。 我正在尋找一種更通用的方式來執行此操作,因為我不知道我將擁有多少個子類。

我嘗試了一些動態轉換,但我讀到它在C#中是不可能的。 這是我的嘗試:

Type type = Items[key].GetType();
Debug.Log(type()); //WeaponModel but I have no access to the method/property

Item item = (type) Items[key]; // Cast don't work
Debug.Log(item.CloseTargetBehavior) // Still no access to subclass property

Item item = (WeaponModel) Items[key]; // Cast works because it is a declarative type
Debug.Log(item.CloseTargetBehavior) // I have ccess to subclass property

問題是要提供所有項目的單個全局列表,因此這將完全解決該情況。

要存儲項目:

public static List<Item> GlobalItems = new List<Item>();

為了訪問派生類型的屬性,必須將Item強制轉換為其派生類型。

要確定其當前類型,請使用:

var itemType = item.GetType();

要將項目用作派生類型,請執行以下操作:

Item item = GlobalItems[0];
DerivedItem derivedItem = item as DerivedItem;
if(derivedItem == null)
{
    Console.WriteLine("Item is not a DerivedItem");
}

要從列表中獲取特定類型的所有項目:

var derivedItems = GlobalItems.Where(item => item.GetType() == typeof(DerivedItem)).Select(item => (DerivedItem)item);

我相信這里真正的問題是用於離線存儲數據的串行器/解串器。 無論做出什么選擇,解串器都必須了解類型,以便在導入保存數據時重新創建正確類型的對象。 .Net XML和Binary序列化程序都存儲詳細的類型信息,並反序列化為正確的類型。

如果用於保存所有數據的主容器(序列化/反序列化的根對象)具有要存儲的每種派生類型的List <>,則可以對存儲的數據進行類型化訪問,而無需花很多時間。 我根本看不到使用字典的意義,因為您將項目ID和所有者ID存儲在Item基類中,並且可以在反序列化后掃描列表以將項目重新附加到其關聯所有者。

The only thing you MIGHT want a dictionary for is to create an ItemContainer 
object with a common/generic method for adding items like: 

Container.AddItem<WeaponModel>(WeaponModel item); 

or accessing the lists based on the derived type 

Container.GetList<WeaponModel>()
Container.GetItemByID<WeaponModel>(int id)

so that you don't have to explicitly create a container class with all the typed lists.. 

simply adding a derived object using these methods would create a list of the 
derived type and add/remove/access data from it in a transparent manner, 
and it would still work correctly with the serializer/deserializer.

編輯:根據要求,提供一種通用技術的快速示例,用於存儲以強類型方式訪問的,從Item派生的任何類型的數據:

using System;
    using System.Collections.Generic;
    public class Item
    {
        public int Id { get; set; }
        public Vector2 GeoCoord { get; set; }
        public int OwnerId { get; set; }
        public string State { get; set; }
        public string Name { get; set; }
        public Type Type { get; set; }
    }

    public class ItemContainer
    {
        private Dictionary<Type, object> items;

        public ItemContainer()
        {
            items = new Dictionary<Type, object>();
        }

        public T Get<T>(int id) where T: Item
        {
            var t = typeof(T);
            if (!items.ContainsKey(t)) return null;
            var dict = items[t] as Dictionary<int, T>;
            if (!dict.ContainsKey(id)) return null;
            return (T)dict[id];
        }

        public void Set<T>(T item) where T: Item
        {
            var t = typeof(T);
            if (!items.ContainsKey(t))
            {
                items[t] = new Dictionary<int, T>();
            }
            var dict = items[t] as Dictionary<int, T>;
            dict[item.Id] = item;
        }

        public Dictionary<int, T> GetAll<T>() where T : Item
        {
            var t = typeof(T);
            if (!items.ContainsKey(t)) return null;
            return items[t] as Dictionary<int, T>;
        }
    }

只是為Ascendion的答案(他因撰寫而獲得全部功勞)添加了一種可能性,還可以添加以下方法:

    public Dictionary<int, T> GetAll<T>() where T: Item
    {
        var t = typeof(T);
        if (!items.ContainsKey(t)) return null;
        var derivedItemDict = new Dictionary<int,T>();
        foreach(var item in items[t]) {
            derivedItemDict.Add(item.Key, (T)item.Value);
        }
        return derivedItemDict;
    }

然后,您可以例如執行以下操作:

Items.GetAll<Weapon>();

為您返回Dictionary<int, Weapon> 。即,它將返回您我認為您要搜索的強類型詞典。 但是請注意,如果您經常使用此功能,則應將其結果確實保存在更改之間,這樣就不必重復執行所有這些強制轉換。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM