加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 为int,float等类型分配其他属性以供反射

发布时间:2020-12-16 01:27:48 所属栏目:百科 来源:网络整理
导读:我试图自动化我的变量的显示(通过反射收集),这些变量位于Unity的特定脚本中.问题是分配自定义值(例如:“string DisplayName”,“bool DisplayMe”,“bool WriteMe”等).当谈到我的自定义类时,我理解我将如何做,但我想避免为此目的重新构建float,string,int
我试图自动化我的变量的显示(通过反射收集),这些变量位于Unity的特定脚本中.问题是分配自定义值(例如:“string DisplayName”,“bool DisplayMe”,“bool WriteMe”等).当谈到我的自定义类时,我理解我将如何做,但我想避免为此目的重新构建float,string,int等类型.

例如,假设我有:

public class myBaseClass
{
    public string Name = "Display Name";
    public bool AmReadable = true;
    public bool AmWritable = true;
}

然后:

public class myDoubleFloat: myBaseClass
{
    public float ValueFirst;
    public float ValueSecond;
}

所以在Unity的一些脚本中我定义它:

public class SomeScriptOnGameObject : MonoBehaviour
{
    public myDoubleFloat myFirstVariable{get; set;}
    public float mySecondVariable{get; set;}
}

所以稍后用反射我可以检查是否应该读取“myFirstVariable”,它的显示名称等等 – 而对于“mySecondVariable”我不能执行此检查.如何在不重新发明轮子的情况下为每个类型创建一个类,如float,int,List等?

解决方法

包装值对象(int,float等)可能不是最好的方法.除了额外的复杂性(以及错误的可能性),你现在正在膨胀游戏的内存占用.

(我在这些例子中故意避免使用更新的C#语法)

由于您已经处于反射上下文中,而不是包装您的值对象,我建议使用基于属性的方法.例如:

public class SomeScriptOnGameObject
{
    [DisplayName("First Variable"),Writable]
    public float FirstVariable { get; set; }

    [DisplayName("Second Variable")]
    public float SecondVariable { get; set; }

    [DisplayName("Some Field")]
    public float Field;

    public float FieldWithNoAttributes;
}

这样做的好处是可以在元数据中保留字段的元数据,而不是在您创建的每个实例中携带所有内容的副本.

实际属性也很容易创建.我将从最简单的一个开始,WritableAttribute:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class WritableAttribute : Attribute
{
}

这个空类是将字段或属性标记为“可写”所需的全部内容. AttributeUsage将此标记为仅对字段和属性有效(例如,不是类).

另一个属性DisplayName只是稍微复杂一些:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class DisplayNameAttribute : Attribute
{
    public string DisplayName { get; private set; }

    public DisplayNameAttribute(string displayName)
    {
        DisplayName = displayName;
    }
}

主要区别在于具有displayName参数的构造函数和DisplayName属性.这会强制编译器期望该属性的参数.

使用一些扩展方法,您可以使事情变得非常干净:

public static class AttributeExtensions
{
    public static bool IsWritable(this MemberInfo memberInfo)
    {
        return memberInfo.GetCustomAttributes(typeof(WritableAttribute)).Any();
    }

    public static string DisplayName(this MemberInfo memberInfo)
    {
        var displayNameAttribute =
            memberInfo.GetCustomAttributes(typeof(DisplayNameAttribute))
                .FirstOrDefault() as DisplayNameAttribute;
        return displayNameAttribute == null ? null : displayNameAttribute.DisplayName;
    }

    public static PropertyInfo Property<T>(this T _,string propertyName)
    {
        return typeof(T).GetProperty(propertyName);
    }

    public static FieldInfo Field<T>(this T _,string fieldName)
    {
        return typeof(T).GetField(fieldName);
    }
}

(既然你提到你已经在使用反射,那么你可能不需要最后两种方法.)

最后,一个简单的XUnit测试来演示:

public class UnitTest1
{
    [Fact]
    public void Test1()
    {
        var obj = new SomeScriptOnGameObject();

        Assert.True(obj.Property("FirstVariable").IsWritable());
        Assert.False(obj.Property("SecondVariable").IsWritable());
        Assert.False(obj.Field("Field").IsWritable());

        Assert.Equal("First Variable",obj.Property("FirstVariable").DisplayName());
        Assert.Equal("Second Variable",obj.Property("SecondVariable").DisplayName());
        Assert.Equal("Some Field",obj.Field("Field").DisplayName());

        Assert.Null(obj.Field("FieldWithNoAttributes").DisplayName());
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读