如果它可以是几种类型之一,如何存储类?

时间:2015-01-04 07:00:30

标签: c# .net generics unity3d

我的项目存在设计问题。

我有3个课程:PlanetStarShip。这些类附加到相应的预制件并包含名称,位置,大小等数据。Planet预制件附加Planet类,而不是其他类。 Star预制件有Star级,Ship预制件附加Ship级。

还有一个名为SelectObject的类,它处理选择功能(当用户点击预制件时)。基本上它显示在所选对象的小窗口图像中,并且从附加到预制件的相应类中拉出一些数据。此类附加到所有可点击/可选对象:PlanetStarShip

现在,我试图让它变得聪明并尝试使用泛型,以便SelectObject可以处理任何当前和未来的类。当我只将Planet对象作为可选对象时,我声明:

public Planet planet;

如果选择了行星,请将数据分配给此var,然后使用此var填充迷你窗口上的信息。 但现在我有3个类,我不想'硬编码'并为每个添加类创建新的变量。我想使用一个变量,它可以是3个(以及将来可能更多)类中的任何一个。

起初我以为我会声明public System.Object myObject;并使用泛型来分配任何内容:

public System.Object myObject;
public T Set<T>(T param)
{
    myObject = param;
    return param;
}

这可以单向运行 - 没有问题分配给一个对象,但是然后将其检索回Planet我得到InvalidCastException: Cannot cast from source type to destination type

那么,对于一个类有3个变量并且有一个重载方法来返回一个非空的唯一的方法吗?或者有一些奇特的方式吗?

PS:经过一番研究后,我发现了.NET 4.0的dynamic关键字,我认为这可以解决我遇到的问题。但似乎Unity不支持4.0?

感谢您的任何见解。

编辑1: 澄清我的项目目前正在发生的事情。我有3个预制件:船,星,星球。每个预制件都附加了SelectObject类。每个预制件还有一个附加在其上的相应数据类,包含在预制类中。最后,每个预制件都附有2个等级。另一个脚本使用行星/船舶/星星数据加载数据文件,实例化相应的预制件并在预制件中填充相应的数据类。

SelectObject具有OnMouseDown函数,当用户点击其中一个实例化的预制件时,它应该从GameObject中提取数据类。这是我遇到问题时:((Prefab)(selObj.GetComponent(typeof(Prefab)))).myObject myObject属于System.Object类型,并且应该包含Ship,Planet或Star类型。但是,当我尝试从Object转换回它应该是的类时,我得到上面提到的错误消息。

进一步增加了混乱:如果我有一个简单的测试脚本,如:

public string myObjectType;
public System.Object myObject;

...

Planet myPlanet = new Planet();
myPlanet.Name = "whatever";
myPlanet.Size = "BIG";
myObject = myPlanet;

...

稍后我尝试从Planet中提取Object类,它的作用为((Planet)myObject).Name。但是,当我从Object中提取GameObject时有更复杂的方法时,它会失败......我不知道为什么。

编辑2:

我发现为什么我从System.Object的初始转换回到相应的类型失败了。当我实例化GameObject时,System.Object变量似乎失去了它的类型。这很奇怪,因为Planet类型的另一个变量很好。我将进一步调查,但到目前为止看起来如果我实例化一个附加了类的GameObject,并且该类有一个System.Object变量并且你为它分配了一些东西,那么在实例化过程中会丢失一些东西。

因此,解决问题的快速方法是:在加载数据和实例化预制件时,在调用Instantiate函数以物理创建GameObject之后,再次分配System.Object变量。这是代码的片段:

// this is where I create a GameObject. Its not instantiated yet. System.Object variable inside newPlanet3 is assigned and correct
GameObject newPlanet3 = CreateStellarObject(myPlanet);
// this piece actually instantiates the object and places in the proper position
GameObject newPlanet2 = Instantiate(newPlanet3, myPlanet.position, Quaternion.identity) as GameObject;
// this is the fix. After instantiate, System.Object variable inside newPlanet2 is lost. Need to reassign it.
((Prefab)(newPlanet2.GetComponent(typeof(Prefab)))).myObject = ((Prefab)(newPlanet3.GetComponent(typeof(Prefab)))).myObject;

我不明白为什么会这样。实例化后我不需要重新分配其他变量。

2 个答案:

答案 0 :(得分:1)

Please check this solution. 我创建了抽象基类,它使用反射来获取/设置字段或属性。

using System;
using System.Reflection;    

public abstract class B
{
    private Type _type;

    protected B()
    {
        _type = GetType();
    }

    public object Get(string name)
    {

        object data = null;

        var field = _type.GetField(name);
        if(field != null)
        {
            data = field.GetValue(this);
        }
        else
        {
            var member = _type.GetProperty(name);
            if(member != null)
            {
                data = member.GetValue(this);
            }
        }

        return data;
    }

    public T Get<T>(string name)
    {
        var data = Get(name);

        if(data != null && data is T)
            return (T)data;

        return default(T);
    }

    public void Set<T>(string name, T data)
    {
        var field = _type.GetField(name);
        if(field != null)
        {
            field.SetValue(this, data);
        }
        else
        {
            var member = _type.GetProperty(name);
            if(member != null)
            {
                member.SetValue(this, data);
            }
        }
    }
}

public class B1 : B
{
    public int Info1 {get; set;}
}

public class B2 : B
{
    public string Info2;
}

public class C
{
    public object mObject;
}

public class Program
{
    public static void Main()
    {
        var b1 = new B1();
        var b2 = new B2();
        b1.Info1 = 1;
        b2.Info2 = "sad";

        Console.WriteLine(b1.Get<int>("Info1"));
        Console.WriteLine(b2.Get("Info2"));

        Console.WriteLine("\r\n\r\n");

        var c  = new C();
        c.mObject = new B1();
        (c.mObject as B).Set("Info1", 123);
        Console.WriteLine((c.mObject as B).Get("Info1"));
    }
}

答案 1 :(得分:0)

您需要一个通用界面,只需为您的对象提供所需的类型即可。也就是说,该类型将允许您对它们进行分组并处理它们而不关心它是什么。

示例:

 public interface DragableObject
 {
     void DragMe();
 }

 public class Star : MonoBehaviour, DragableObject
 {
     public void DragMe()
     {
         print("I am a Star, I don't move easily.");
     }
 }

 public class Ship : MonoBehaviour, DragableObject
 {
     public void DragMe()
     {
         print("Engaging warp speed.");
     }
 }

有一个维护这些对象的类。

public class GameManager
{
     private DragableObject [] list;
}

然后你可以简单地调用DragMe(),每个对象都应该担心它们是如何处理的。

list[0].DragMe();