简体   繁体   English

当派生对象属性存储在父类型集合中时,如何访问它们?

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

I works on a RPG and I am stuck on how to make a global list of all items of the world. 我从事RPG方面的工作,并且对如何制作世界上所有物品的全球清单感到困惑。 I works with Unity3D. 我使用Unity3D。

My items could be weapons, currency, macscots and dozen of subderived class. 我的物品可能是武器,货币,Macscots和许多衍生类。

So I have a base Class Item (simplified code) 所以我有一个基本的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;}
}

and sub classes 和子类

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;}
    ...    
}

For saving purpose and others reasons, I would like to store all the instances of those derived class in a dictionary (or anything else). 出于节省目的和其他原因,我想将这些派生类的所有实例存储在字典(或其他任何东西)中。 But I read it is not recommended to store multi typed objects in a collection because you can only access their common properties (here Item's class properties). 但我读到它不建议将多类型对象存储在集合中,因为您只能访问它们的公共属性(此处为Item的类属性)。

So what is the best way to have a global list of all sort of objects and still get access to their properties? 那么,拥有所有对象的全局列表并仍然可以访问其属性的最佳方法是什么?

EDIT 1 : Added some details on usage 编辑1:添加了一些用法详细信息

My first use for this collection is to use it as a description for some "factories": 该集合的第一个用途是将其用作一些“工厂”的描述:

1)When the game starts, I deserialize a JSON files which build this unique collection. 1)当游戏开始时,我反序列化一个JSON文件来构建这个唯一的集合。 I use http://www.dustinhorne.com/post/Json-NET-for-Unity-Developers I use a settings.TypeNameHandling = TypeNameHandling.All; 我使用http://www.dustinhorne.com/post/Json-NET-for-Unity-Developers。我使用settings.TypeNameHandling = TypeNameHandling.All; parameter to keep all class/subclasses description. 保留所有类/子类描述的参数。

2)Different concrete GameObject, which could be assimilated as a View element, are made to use in 2Dmap, 3Dworld, inventory... but each representation refers to the subclass instance (WeaponModel or MascotModel or...). 2)不同的具体GameObject(可以同化为View元素)被用于2Dmap,3Dworld,清单...中,但是每个表示形式都引用子类实例(WeaponModel或MascotModel或...)。 Eg : a clic on an item on the map, or in the 3D view will both change the state of the Model. 例如:对地图上某个项目或3D视图中的项目的更改都会改变模型的状态。

I thought about "if(typeOf(WeaponModel)) else if..." But this is the start point of my confusion. 我想到了“ if(typeOf(WeaponModel))else if ...”,但这是我困惑的起点。 I am looking a more generic way to do this because I don't know how many subclass I will have. 我正在寻找一种更通用的方式来执行此操作,因为我不知道我将拥有多少个子类。

I tried some dynamic casting, but I read it is not possible in C#. 我尝试了一些动态转换,但我读到它在C#中是不可能的。 This is my try : 这是我的尝试:

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

The question is asking for a single global list of all items, so this will handle exactly that scenario. 问题是要提供所有项目的单个全局列表,因此这将完全解决该情况。

To store the items: 要存储项目:

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

In order to get access to the properties of a derived type, you will have to cast the Item to it's derived type. 为了访问派生类型的属性,必须将Item强制转换为其派生类型。

To determine it's current type, use: 要确定其当前类型,请使用:

var itemType = item.GetType();

To use an item as it's derived type: 要将项目用作派生类型,请执行以下操作:

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

To get all items from the list which are a certain type: 要从列表中获取特定类型的所有项目:

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

I believe the real issue here is the serializer/deserializer used to store the data offline. 我相信这里真正的问题是用于离线存储数据的串行器/解串器。 Whatever choice is made, the deserializer has to be Type aware so it recreates objects of the correct type when the save data is imported. 无论做出什么选择,解串器都必须了解类型,以便在导入保存数据时重新创建正确类型的对象。 The .Net XML and Binary serializers both store detailed type information and deserialize to the correct types. .Net XML和Binary序列化程序都存储详细的类型信息,并反序列化为正确的类型。

If your primary container (the root object that you serialize/deserialize) for saving all the data had a List<> for each derived type that you wanted to store, you would get typed access to the stored data without jumping through a lot of hoops. 如果用于保存所有数据的主容器(序列化/反序列化的根对象)具有要存储的每种派生类型的List <>,则可以对存储的数据进行类型化访问,而无需花很多时间。 I don't see the point of using a dictionary at all since you store item and owner IDs in the Item base class and you can scan the lists after deserializing to reattach the items to their associated owners. 我根本看不到使用字典的意义,因为您将项目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.

EDIT: as requested a quick example of a generic technique to store data of any type derived from Item that is accessible in strongly typed fashion: 编辑:根据要求,提供一种通用技术的快速示例,用于存储以强类型方式访问的,从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>;
        }
    }

Just to add a possibility to Ascendion's answer (who gets all the credit for writing it up), you could also add the method: 只是为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;
    }

And then you could, eg, do: 然后,您可以例如执行以下操作:

Items.GetAll<Weapon>();

to return a Dictionary<int, Weapon> for you.. Ie it would return you the strongly-typed dictionary I believe you seek. 为您返回Dictionary<int, Weapon> 。即,它将返回您我认为您要搜索的强类型词典。 But note that if you use this a lot, the result of this should really be saved separately between changes so you don't have to repetitively do all these casts. 但是请注意,如果您经常使用此功能,则应将其结果确实保存在更改之间,这样就不必重复执行所有这些强制转换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何访问存储在基类列表中的派生类中的属性 - how to access properties in derived class stored in a list of the base class 创建基类的集合,然后在集合项中访问派生类的属性 - Creating a collection of base class, then access properties of derived classes in the collection items 如何访问存储在对象列表中的对象属性c# - how to access object's properties stored in list of objects c# 访问基类集合中的派生类属性 - Access Derived class properties from base class collection OData V3:如何查询存储在父表中的派生类型? - OData V3: how to query derived type when it is stored in a parent table? 如何将.NET对象集合(父级-子级)层次结构传递给SQL Server存储过程 - How to pass .NET Collection of objects (Parent-Child) hierarchy to a SQL Server stored procedure 当父属性更改时,为派生属性触发 PropertyChanged - Fire PropertyChanged for derived properties when parent property changes 在集合中保存派生类属性 - Keeping Derived Class Properties in a Collection 如何用标志枚举属性展平对象集合? - How to Flatten a Collection of Objects with Flag Enum Properties? INotifyPropertyChanged 和不同对象上的派生属性 - INotifyPropertyChanged And derived properties on different objects
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM