RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1591174
Accepted
FruitGames537
FruitGames537
Asked:2024-08-19 08:32:20 +0000 UTC2024-08-19 08:32:20 +0000 UTC 2024-08-19 08:32:20 +0000 UTC

FindPropertyRelative 方法不搜索对象类型的序列化字段

  • 772

我有一个类Container<TKey, TValue>,它是一个带有键TKey和值的对象,可以是TValue相同容器的列表:

using System.Collections.Generic;
using System;
using UnityEngine;

namespace Adapter.Containers
{
    [Serializable]
    public class Container<TKey, TValue>
    {
        public Container() : this(default) { }
        public Container(TKey key) : this(key, default) { }
        public Container(TKey key, TValue value)
        {
            m_Key = key;
            m_Value = value;
        }



        [SerializeField] private TKey m_Key;
        [SerializeField] private object m_Value;

        public object this[TKey name]
        {
            get
            {
                if (GetContainer(name) is Container<TKey, TValue> container)
                    return container;
                else if (GetStore(name) is TValue elementary)
                    return elementary;
                throw new ArgumentException("Cannot get value because store unknown type");
            }
            set
            {
                if (value is Container<TKey, TValue> container)
                    SetContainer(name, container);
                else if (value is TValue elementary)
                    SetStore(name, elementary);
                throw new ArgumentException("Cannot set value because this type not support");
            }
        }

        public TKey name { get => m_Key; set => m_Key = value; }

        public List<Container<TKey, TValue>> container
        {
            get
            {
                if (m_Value is List<Container<TKey, TValue>>)
                    return m_Value as List<Container<TKey, TValue>>;
                return null;
            }
            set
            {
                if (value is not null)
                    m_Value = value;
                throw new ArgumentException("Cannot set not container value by container property");
            }
        }
        public TValue store
        {
            get
            {
                if (m_Value is TValue)
                    return (TValue)m_Value;
                return default;
            }
            set
            {
                if (value is not null)
                    m_Value = value;
                throw new ArgumentException("Cannot set unacceptable type value by store property");
            }
        }

        public ContainerType type => m_Value is List<Container<TKey, TValue>> ? ContainerType.Container : m_Value is TValue ? ContainerType.Store : ContainerType.Unknown;



        public Container<TKey, TValue> GetContainer(TKey name) => container[container.FindIndex(item => item.name.Equals(name))]; // Получение по индексу просто для эстетичности
        public void SetContainer(TKey name, Container<TKey, TValue> value) => container[container.FindIndex(item => item.name.Equals(name))] = value;

        public TValue GetStore(TKey name) => container.Find(item => item.name.Equals(name)).store;
        public void SetStore(TKey name, TValue value) => container.Find(item => item.name.Equals(name)).store = value;
    }
}

我还有一个ContainerPropertyDrawer用于渲染此类的类:

using UnityEditor;
using UnityEngine;

namespace Adapter.Containers.Editor
{
    [CustomPropertyDrawer(typeof(Container<,>))]
    public class ContainerPropertyDrawer : PropertyDrawer
    {
        private ContainerType m_ContainerType = ContainerType.Unknown;

        public ContainerType containerType { get => m_ContainerType; set => m_ContainerType = value; }



        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            SerializedProperty keyProperty = property.FindPropertyRelative("m_Key");
            SerializedProperty valueProperty = property.FindPropertyRelative("m_Value");

            EditorGUI.LabelField(position, label);
            EditorGUILayout.PropertyField(keyProperty);
            m_ContainerType = (ContainerType)EditorGUILayout.EnumPopup(m_ContainerType);
            if (m_ContainerType != ContainerType.Unknown)
                if (m_ContainerType == ContainerType.Container)
                    EditorGUILayout.PropertyField(valueProperty, true);
                else
                    EditorGUILayout.ObjectField(valueProperty.objectReferenceValue, typeof(object), true);
        }
    }
}

我遇到的问题是,首先,它会更改同一列表中的所有元素(不是特别重要),其次,不会为我显示值字段(当我尝试绘制该字段时m_ContainerType会出现错误,据我所知,unity 不想正常接收具有或不具有类型的字段?)SerializedProperty is nullm_Valueobject

c#
  • 2 2 个回答
  • 48 Views

2 个回答

  • Voted
  1. Yaroslav
    2024-08-20T02:01:34Z2024-08-20T02:01:34Z

    序列化不保留对象的类型!只有数据,这些只是“名称:值”对,愚蠢的json。


    例子:

    [Serializable]
    public class Animal 
    {
        public string Name;
    
        public Animal (string name)
            Name => name;
    }
    
    [Serializable]
    public class Cat : Animal
    {
        public string Breed;
    
        public Cat (string name, string breed) : base(name)
            Breed=> breed;
    }
    
    [Serializable]
    public class Test
    {
       public Animal MyAnimal;
       public Cat MyCat;
    }
    
    var t = new Test()
    {
        MyAnimal = new Cat("Jafar", "Persian"),
        MyCat = new Cat("Willem", "Siamese")
    };
    Debug.Log(JsonUtility.ToJson(t));
    

    结果:

    {
      "MyAnimal": {
        "Name": "Jafar"
      },
      "MyCat": {
        "Name": "Willem",
        "Breed": "Siamese"
      }
    }
    

    正如您所看到的,尽管MyAnimal我们指定了类型为 的对象Cat,但没有有关该字段的信息Breed,因为类型是Animal!

    在去阿里化过程中,根据字段类型重新创建对象。但我们根本无法序列化它,System.Object不仅因为它不是[Serializable],而且因为它不包含任何数据,只有方法 ToString、GetHashCode和。GetTypeEquals


    如果Animal有Test或MonoBehaviour,ScriptableObject即UnityEngine.Object,那么在检查员中我们可以指出他的任何继承人。但与上面的例子不同的是,这个Animal不会在上下文中重新创建Test,它将独立于上下文创建,并且以Test值的形式存在一个指向id的指针UnityEngine.Object。

    这个过程可以在开发过程中观察到。有时我们重命名组件,脚本的链接在检查器中消失了,但是数据并没有消失,从中我们可以理解,在检查器中我们根本不是在处理一个类,而是在处理一个建立在该类上的数据模型,这就是它的作用SerializedProperty。您不仅可以选择具有新名称的脚本,还可以选择任何一般脚本,并且它将具有名称匹配的那些字段的所有值。


    SerializedProperty.objectReferenceValue仅包含UnityEngine.Object,这可以通过将鼠标悬停在该属性上来看到,将其强制转换为 base 是没有意义的System.Object。ObjectField当然,它也不起作用;在编辑器中,Unity所有引用类型都只能是 from UnityEngine.Object,因为Unity它只处理它的存储和生命周期。目前还不清楚如何处理这个typeof(object),也不清楚它是谁的以及和什么一起吃。


    您将无法从中序列化结构Container<TKey, TValue>;您必须有一个类型化模型才能将数据反序列化到其中。你写的东西已经存在了,就是这样json。有,您可以在AssetStore其中JSON Object对值进行操作,并且有一些方法可以帮助确定它们的类型,例如IsFloat,但是没有检查器,因为......为什么需要它?这只是文本,您可以手写。

    在某个通用适配器的框架内,更容易使用路径对,例如"vector.direction">"Rotation"并根据给定方案重建json以创建所需的对象。这里根本不需要检查器,除非您可以从这些对中注册一个方案,而不需要任何值。但这也很奇怪,因为这只是程序员的工作,对他们来说用代码描述这些方案应该不难,并且检查员不会以任何方式加速或促进这项工作。

    • 2
  2. Best Answer
    FruitGames537
    2024-08-20T11:26:37Z2024-08-20T11:26:37Z

    为了解决这个问题,我只需创建一个新的结构Variation<TKey, TValue>(这就是为什么c#中没有像c++中这样的类?(std::variant))并object用type 替换 type Variation<TValue, List<Container<TKey, TValue>>,最后类变成这样:

    容器.cs:

    using System.Collections.Generic;
    using System;
    using UnityEngine;
    
    namespace Adapter.Containers
    {
        [Serializable]
        public class Container<TKey, TValue>
        {
            public Container() : this(default) { }
            public Container(TKey key) : this(key, default) { }
            public Container(TKey key, TValue value)
            {
                m_Key = key;
                m_Value = value;
            }
    
    
    
            [SerializeField] private TKey m_Key;
            [SerializeField] private Variation<TValue, List<Container<TKey, TValue>>> m_Value;
    
            public Variation<TValue, Container<TKey, TValue>> this[TKey name]
            {
                get
                {
                    if (GetContainer(name) is Container<TKey, TValue> container)
                        return container;
                    else if (GetStore(name) is TValue elementary)
                        return elementary;
                    throw new ArgumentException("Cannot get value because store unknown type");
                }
                set
                {
                    if (value.value is Container<TKey, TValue> container)
                        SetContainer(name, container);
                    else if (value.value is TValue elementary)
                        SetStore(name, elementary);
                    throw new ArgumentException("Cannot set value because this type not support");
                }
            }
    
            public TKey name { get => m_Key; set => m_Key = value; }
    
            public List<Container<TKey, TValue>> container
            {
                get
                {
                    if (m_Value.value is List<Container<TKey, TValue>>)
                        return m_Value.value as List<Container<TKey, TValue>>;
                    return null;
                }
                set
                {
                    if (value is not null)
                        m_Value.value = value;
                    throw new ArgumentException("Cannot set not container value by container property");
                }
            }
            public TValue store
            {
                get
                {
                    if (m_Value.value is TValue)
                        return (TValue)m_Value.value;
                    return default;
                }
                set
                {
                    if (value is not null)
                        m_Value.value = value;
                    throw new ArgumentException("Cannot set unacceptable type value by store property");
                }
            }
    
            public ContainerType type => m_Value.value is List<Container<TKey, TValue>> ? ContainerType.Container : m_Value.value is TValue ? ContainerType.Store : ContainerType.Unknown;
    
    
    
            public Container<TKey, TValue> GetContainer(TKey name) => container[container.FindIndex(item => item.name.Equals(name))];
            public void SetContainer(TKey name, Container<TKey, TValue> value) => container[container.FindIndex(item => item.name.Equals(name))] = value;
    
            public TValue GetStore(TKey name) => container.Find(item => item.name.Equals(name)).store;
            public void SetStore(TKey name, TValue value) => container.Find(item => item.name.Equals(name)).store = value;
        }
    }
    

    变体.cs:

    using System;
    using UnityEngine;
    
    namespace Adapter.Containers
    {
        [Serializable]
        public struct Variation<T1, T2>
        {
            private Variation(T1 oneValue, T2 twoValue, Type storeType, VariationType type)
            {
                if (typeof(T1) == typeof(T2))
                    throw new ArgumentException("Cannot create variation because type one and type two not different");
                m_OneValue = oneValue;
                m_TwoValue = twoValue;
                m_StoreType = storeType;
                m_Type = type;
            }
            public Variation(T1 value) : this(value, default, typeof(T1), VariationType.One) { }
            public Variation(T2 value) : this(default, value, typeof(T2), VariationType.Two) { }
    
    
    
            [SerializeField] private T1 m_OneValue;
            [SerializeField] private T2 m_TwoValue;
    
            [SerializeField] private Type m_StoreType;
            [SerializeField] private VariationType m_Type;
    
            public T1 oneValue
            {
                get
                {
                    if (m_Type is VariationType.One)
                        return m_OneValue;
                    return default;
                }
                set
                {
                    m_OneValue = value;
                    m_TwoValue = default;
                    m_StoreType = typeof(T1);
                    m_Type = VariationType.One;
                }
            }
            public T2 twoValue
            {
                get
                {
                    if (m_Type is VariationType.Two)
                        return m_TwoValue;
                    return default;
                }
                set
                {
                    m_OneValue = default;
                    m_TwoValue = value;
                    m_StoreType = typeof(T2);
                    m_Type = VariationType.Two;
                }
            }
    
            public Type storeType => m_StoreType;
            public VariationType type => m_Type;
    
            public object value
            {
                get
                {
                    return m_StoreType is T1 ? m_OneValue : m_TwoValue;
                }
                set
                {
                    if (value is T1)
                        oneValue = (T1)value;
                    else if (value is T2)
                        twoValue = (T2)value;
                    else
                        throw new InvalidOperationException("Cannot set value because value type is unacceptable variation type");
                }
            }
    
    
    
            public static implicit operator Variation<T1, T2>(T1 value) => new Variation<T1, T2>(value);
            public static implicit operator Variation<T1, T2>(T2 value) => new Variation<T1, T2>(value);
            public static implicit operator T1(Variation<T1, T2> value) => value.oneValue;
            public static implicit operator T2(Variation<T1, T2> value) => value.twoValue;
        }
    }
    

    ContainerPropertyDrawer.cs:

    using UnityEditor;
    using UnityEngine;
    
    namespace Adapter.Containers.Editor
    {
        [CustomPropertyDrawer(typeof(Container<,>))]
        public class ContainerPropertyDrawer : PropertyDrawer
        {
            private float singleLine => EditorGUIUtility.singleLineHeight;
            private float spacing => EditorGUIUtility.standardVerticalSpacing;
    
    
    
            public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
            {
                EditorGUI.BeginProperty(position, label, property);
    
                EditorGUI.LabelField(new Rect(position.x, position.y, position.width, singleLine), label);
    
                if (property.FindPropertyRelative("m_Key") is SerializedProperty keyProperty)
                    EditorGUI.PropertyField(new Rect(position.x, position.y += singleLine + spacing, position.width, EditorGUI.GetPropertyHeight(keyProperty)), keyProperty);
                if (property.FindPropertyRelative("m_Value") is SerializedProperty valueProperty)
                    EditorGUI.PropertyField(new Rect(position.x, position.y += singleLine + spacing, position.width, EditorGUI.GetPropertyHeight(valueProperty)), valueProperty);
    
                EditorGUI.EndProperty();
            }
    
            public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
            {
                float height = singleLine + spacing;
    
                if (property.FindPropertyRelative("m_Key") is SerializedProperty keyProperty)
                    height += EditorGUI.GetPropertyHeight(keyProperty);
                if (property.FindPropertyRelative("m_Value") is SerializedProperty valueProperty)
                    height += EditorGUI.GetPropertyHeight(valueProperty);
                if (property.FindPropertyRelative("m_Key") is not null && property.FindPropertyRelative("m_Value") is not null)
                    height += spacing;
    
                return height;
            }
        }
    }
    

    VariationPropertyDrawer.cs:

    using UnityEditor;
    using UnityEngine;
    
    namespace Adapter.Containers.Editor
    {
        [CustomPropertyDrawer(typeof(Variation<,>))]
        public class VariationPropertyDrawer : PropertyDrawer
        {
            private float singleLine => EditorGUIUtility.singleLineHeight;
            private float spacing => EditorGUIUtility.standardVerticalSpacing;
    
    
    
            public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
            {
                EditorGUI.BeginProperty(position, label, property);
    
                EditorGUI.PropertyField(new Rect(position.x, position.y, position.width, singleLine), property.FindPropertyRelative("m_Type"));
                VariationType type = (VariationType)property.FindPropertyRelative("m_Type").intValue;
    
                if (type is VariationType.One && property.FindPropertyRelative("m_OneValue") is SerializedProperty oneValueProperty)
                    EditorGUI.PropertyField(new Rect(position.x, position.y += singleLine + spacing, position.width, EditorGUI.GetPropertyHeight(oneValueProperty)), oneValueProperty, new GUIContent("Value"));
                else if (type is VariationType.Two && property.FindPropertyRelative("m_TwoValue") is SerializedProperty twoValueProperty)
                    EditorGUI.PropertyField(new Rect(position.x, position.y += singleLine + spacing, position.width, EditorGUI.GetPropertyHeight(twoValueProperty)), twoValueProperty, new GUIContent("Value"));
    
                EditorGUI.EndProperty();
            }
    
            public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
            {
                float height = singleLine + spacing;
    
                VariationType type = (VariationType)property.FindPropertyRelative("m_Type").intValue;
                if (type is VariationType.One && property.FindPropertyRelative("m_OneValue") is SerializedProperty oneValueProperty)
                    height += EditorGUI.GetPropertyHeight(oneValueProperty);
                else if (type is VariationType.Two && property.FindPropertyRelative("m_TwoValue") is SerializedProperty twoValueProperty)
                    height += EditorGUI.GetPropertyHeight(twoValueProperty);
    
                return height;
            }
        }
    }
    

    顺便说一句,这个项目在github上

    • 0

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5