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

时间:2014-09-18 22:03:28

标签: c# inheritance dictionary casting unity3d

我在RPG上工作,我不知道如何制作世界上所有项目的全球列表。我使用Unity3D。

我的物品可能是武器,货币,macscots和十几个次级的。

所以我有一个基类项(简化代码)

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)可以被同化为View元素的不同具体GameObject可用于2Dmap,3Dworld,inventory ......但每个表示都引用子类实例(WeaponModel或MascotModel或...)。例如:地图上的项目或3D视图中的clic都会改变模型的状态。

我想过“if(typeOf(WeaponModel))否则......” 但这是我困惑的起点。我正在寻找一种更通用的方法,因为我不知道我将拥有多少个子类。

我尝试了一些动态转换,但我在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

3 个答案:

答案 0 :(得分:1)

问题是要求所有项目的单个全局列表,因此这将完全处理该场景。

存储物品:

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);

答案 1 :(得分:1)

我认为这里真正的问题是用于离线存储数据的串行器/解串器。无论做出何种选择,反序列化器都必须是类型识别的,因此在导入保存数据时它会重新创建正确类型的对象。 .Net XML和Binary序列化程序都存储详细的类型信息并反序列化为正确的类型。

如果用于保存所有数据的主容器(您序列化/反序列化的根对象)具有List&lt;&gt;对于您想要存储的每个派生类型,您将获得对存储数据的类型化访问,而不会跳过很多箍。由于您在Item基类中存储了项目和所有者ID,因此我根本没有看到使用字典的意义,您可以在反序列化后扫描列表以将项目重新附加到其关联的所有者。

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

答案 2 :(得分:0)

只是为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> ..也就是说,它会返回你认为你寻求的强类型字典。但请注意,如果你经常使用它,那么结果应该在变化之间单独保存,这样你就不必重复做所有这些演员。