【问题标题】:How to store a class if it could be one of several types?如果一个类可以是几种类型之一,如何存储它?
【发布时间】:2015-01-04 07:00:29
【问题描述】:

我的一个项目存在设计问题。

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

这是一种方式 - 分配给一个对象没有问题,然后将它检索回行星我得到InvalidCastException: Cannot cast from source type to destination type

那么,一个类有 3 个变量并有一个重载方法来返回一个非 null 变量是唯一的方法吗?或者有什么奇特的方法吗?

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;

...

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

编辑 2:

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

因此,解决此问题的快速方法是:在加载数据和实例化预制件时,在调用 Instantiate 函数以物理创建游戏对象后,再次分配 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;

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

【问题讨论】:

    标签: c# .net generics unity3d


    【解决方案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"));
        }
    }
    

    【讨论】:

      【解决方案2】:

      您需要一个通用接口,它可以简单地为您的对象提供您想要的类型。也就是说,该类型将允许您对它们进行分组并处理它们,而无需关心它们是什么。

      例子:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-08-30
        • 1970-01-01
        • 2011-11-20
        • 2019-09-01
        • 2017-01-01
        • 1970-01-01
        • 2018-09-12
        相关资源
        最近更新 更多