我的项目存在设计问题。
我有3个课程:Planet
,Star
,Ship
。这些类附加到相应的预制件并包含名称,位置,大小等数据。Planet
预制件附加Planet
类,而不是其他类。 Star
预制件有Star
级,Ship
预制件附加Ship
级。
还有一个名为SelectObject
的类,它处理选择功能(当用户点击预制件时)。基本上它显示在所选对象的小窗口图像中,并且从附加到预制件的相应类中拉出一些数据。此类附加到所有可点击/可选对象:Planet
,Star
和Ship
。
现在,我试图让它变得聪明并尝试使用泛型,以便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;
我不明白为什么会这样。实例化后我不需要重新分配其他变量。
答案 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();