使用通用get方法扩展SerializedObject

时间:2018-02-08 15:30:19

标签: c# templates generics unity3d

我想为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参数而不是特化,但那么这个函数的返回类型应该是什么?

3 个答案:

答案 0 :(得分:2)

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

    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功能。

setter方法的

UPDATE

    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参数并且必须返回一些内容。

答案 1 :(得分:2)

你可以使用通用静态的魔力来实现这一目标。

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

由于GetPropertyValue<int>.Get的静态变量与静态变量GetPropertyValue<string>.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);
    }
}

答案 2 :(得分:1)

虽然不是一个漂亮的解决方案,但一个工作的解决方案就像:

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)

语义保持不变,但实现更清晰。