Обтекание объектов-значений (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());
}
}