【问题标题】:Extending SerializedObject with generic get method使用通用 get 方法扩展 SerializedObject
【发布时间】:2018-02-08 15:30:18
【问题描述】:

我想为SerializedObject 写一个通用扩展方法,可以用来代替FindProperty,然后访问whateverValue 成员,所以我可以写so.Get<Bool>("myValue") 而不是so.FindProperty("myValue").boolValue

如果模板专业化是 C# 中的一个东西,我想如何解决这个问题:

public static T Get<T>(this SerializedObject so, string name) {
    Debug.LogError("Get called with unsuported type!");
}

public static bool Get<bool>(this SerializedObject so, string name) {
    return so.FindProperty(name).boolValue;
}

如何在适当的 C# 中实现这样的目标?我也尝试添加一个System.Type 参数而不是专门化,但是这样的函数的返回类型应该是什么?

【问题讨论】:

    标签: c# templates generics unity3d


    【解决方案1】:

    我会使用一点函数式编程。通用函数的输入参数之一将是另一个函数,它将定义如何读取属性:

        public static T Get<T>(this SerializedObject so, string name, Func<SerializedProperty, T> getter) {
            var property = so.FindProperty(name);
            if (property == null) {
                ;//handle "not found"
            }
            return getter(property);
        }
    

    我将如何使用它的几个示例:

        internal bool ExampleBoolValue(SerializedObject so) {
            return so.Get("myBoolValue", (p => p.boolValue));
        }
    
        internal int ExampleIntValue(SerializedObject so) {
            return so.Get("myIntValue", (p => p.intValue));
        }
    

    我没有在这台机器上安装 Unity,所以我不确定 Unity 是否支持这些 .NET 功能。

    UPDATE 用于 setter 方法:

        public static void Set(this SerializedObject so, string name, Action<SerializedProperty> setter) {
            var property = so.FindProperty(name);
            if (property == null) {
                ;//handle "not found"
            }
            setter(property);
        }
    

    设置值示例:

        internal void SetExampleBoolValue(SerializedObject so, bool newValue) {
            so.Set("myBoolValue", (p => p.boolValue = newValue));
        }
    
        internal void SetExampleIntValue(SerializedObject so, int newValue) {
            so.Set("myIntValue", (p => p.intValue = newValue));
        }
    

    Action 接受 0..n 个参数并且不返回任何内容。 Func 接受 0..n 个参数并且必须返回一些东西。

    【讨论】:

    • 嗯,这个扩展方法的重点是少写,你的解决方案没有做到这一点。
    • 它的优势是拥有可以处理错误和放置断点的中心位置。如果这在项目中不重要,那么一个简单的包装器就足够了,就像@Maxim Zabolotskikh 的回答一样。
    • 由于我正在扩展该功能以使其也能够用作设置器,因此该解决方案可以更好地工作。
    • 您能否修改您的答案,以便它也可以修改SerializedProperty?由于 Unity 无法返回引用,我的简单解决方案是将 T? val = null 作为最后一个参数传递。
    • @MarošBeťko 我已经更新了示例。对于 setter,我没有使用泛型。
    【解决方案2】:

    您可以使用通用静态的魔力来完成此操作。

    第一个类GetPropertyValue&lt;T&gt; 将根据类型存储您的处理程序。静态处理程序最初设置为您的“不受支持”消息,但静态构造函数将调用 InitGetPropertyValue 类来初始化所有处理程序。因为这是在该类的静态构造函数中,所以只会在类第一次初始化时调用一次。

    由于GetPropertyValue&lt;int&gt;.Get 的静态变量与静态变量GetPropertyValue&lt;string&gt;.Get 不同,因此您之前存储的类型的处理程序将在每次后续调用时使用。

    public static class MyExtensions
    {
        private static class GetPropertyValue<T>
        {
            static GetPropertyValue()
            {
                InitGetPropertyValue.Initialize();
            }
    
            public static Func<SerializedObject, string, T> Get = (so, name) =>
             {
                 Debug.Print("Get called with unsupported type!");
                 return default(T);
             };
        }
    
        private static class InitGetPropertyValue
        {
            static InitGetPropertyValue()
            {
                Debug.Print("Initializing property getters");
                GetPropertyValue<int>.Get = (so, name) => (int)so.FindProperty(name) ;
                GetPropertyValue<Guid>.Get = (so, name) => (Guid)so.FindProperty(name);
                GetPropertyValue<string>.Get = (so, name) => so.FindProperty(name).ToString();
            }
    
            public static bool Initialize()
            {
                return true;
            }
        }
    
        public static T Get<T>(this SerializedObject so, string name)
        {
            return GetPropertyValue<T>.Get(so, name);
        }
    }
    

    【讨论】:

    • 我不太明白=&gt;的意思,但我会试一试。
    • 好像 Unity 与 C# 4 一起工作,所以我不能使用这个解决方案。
    • 查看 lambda 函数以进一步了解 =&gt;。我做了一项可能对您有所帮助的更改,但是当您尝试使用此代码时会出现什么错误?
    • 我知道什么是 lambda,我只是不熟悉第 5 行的语法。我得到的错误是您示例第 5 行的 Feature 'expression body constructor and destructor' is not available in C# 4. Please use language version 7 or greater. [Assembly-CSharp]
    • 这是创建 C# 7 及更高版本中可用方法的简写。我将其更改为使用传统语法,因此您可以再试一次。
    【解决方案3】:

    虽然不是一个漂亮的解决方案,但一个可行的解决方案可能是这样的:

    public static T Get<T>(this SerializedObject so, string name) {
        if (typeof(T) == typeof(bool){
            return (T)(object)so.FindProperty(name).boolValue;
        }
        else if {
        ...
        }
        else {
            Debug.LogError("Get called with unsuported type!");
        }
    }
    

    我个人会喜欢这样的东西:

    public static bool GetBoolean(this SerializedObject so, string name)
    public static int GetInt(this SerializedObject so, string name)
    

    语义保持不变,但实现更简洁。

    【讨论】:

    • 我习惯用 C++ 编写代码,使用模板可以很好地编写这些东西。但由于这是 C#,那些 GetBooleanGetInt 函数应该可以很好地工作:D
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-27
    • 1970-01-01
    • 2015-10-19
    相关资源
    最近更新 更多