#c# #generics #custom-attributes
#c# #общие положения #пользовательские атрибуты
Вопрос:
Я написал несколько общий механизм десериализации, который позволяет мне создавать объекты из двоичного формата файла, используемого приложением C .
Чтобы все было чисто и легко изменялось, я создал Field
класс, который расширяется Attribute
, создается с Field(int offset, string type, int length, int padding)
помощью и применяется к атрибутам класса, которые я хочу десериализовать. Вот как это выглядит :
[Field(0x04, "int")]
public int ID = 0;
[Field(0x08, "string", 0x48)]
public string Name = "0";
[Field(0x6C, "byte", 3)]
public byte[] Color = { 0, 0, 0 };
[Field(0x70, "int")]
public int BackgroundSoundEffect = 0;
[Field(0x74, "byte", 3)]
public byte[] BackgroundColor = { 0, 0, 0 };
[Field(0x78, "byte", 3)]
public byte[] BackgroundLightPower = { 0, 0, 0 };
[Field(0x7C, "float", 3)]
public float[] BackgroundLightAngle = { 0.0f, 0.0f, 0.0f };
Затем вызов myClass.Decompile(pathToBinaryFile)
извлекает данные из файла, считывая соответствующие типы и размеры с соответствующими смещениями.
Однако я нахожу, что передавать имя типа в виде строки некрасиво.
Возможно ли передать тип более элегантным, но коротким способом, и как?
Спасибо.
Комментарии:
1. Тип такой же, как у отмеченного поля, не так ли? Почему бы не получить тип из поля, вместо того, чтобы отмечать его в конструкторе атрибута?
Ответ №1:
Используйте typeof
оператор (возвращает экземпляр Type
):
[Field(0x7C, typeof(float), 3)]
Комментарии:
1. @Heandel: Безопасность типов и поддержка рефакторинга в каждом атрибуте по сравнению с шестью дополнительными символами в каждом атрибуте == Я бы нахмурился 🙂
2. @Heandel, это единственный чистый способ сделать это… Это немного длиннее, но в 100 раз лучше, чем использование строки
3. Я просто делю удивительность typeof на удивительность string… как вы можете видеть, это очень точная оценка 😉
Ответ №2:
Да: пусть атрибут принимает a Type
в качестве параметра, а затем передает, например typeof(int)
.
Ответ №3:
Да, параметр должен иметь тип Type
, и тогда вы можете передать тип следующим образом:
[Field(0x7C, typeof(float), 3)]
public float[] BackgroundLightAngle = { 0.0f, 0.0f, 0.0f };
Ответ №4:
Я не думаю, что вам нужно указывать тип в конструкторе атрибута, вы можете получить его из поля. Смотрите пример:
public class FieldAttribute : Attribute { }
class Data
{
[Field]
public int Num;
[Field]
public string Name;
public decimal NonField;
}
class Deserializer
{
public static void Deserialize(object data)
{
var fields = data.GetType().GetFields();
foreach (var field in fields)
{
Type t = field.FieldType;
FieldAttribute attr = field.GetCustomAttributes(false)
.Where(x => x is FieldAttribute)
.FirstOrDefault() as FieldAttribute;
if (attr == null) return;
//now you have the type and the attribute
//and even the field with its value
}
}
}
Комментарии:
1. @Heandel: Возможно, я неправильно понял вашу идею. Но правда в том, что атрибут не может получить информацию о своем «вешателе». Другими словами, у вас есть экземпляр некоторого атрибута с именем
a
, вы не можете определить, какое поле / свойство / классa
помечается.
Ответ №5:
В C # 10 появилась новая функция (в предварительном просмотре на момент публикации этого поста), которая позволяет создавать универсальные атрибуты!
Используя эту новую функцию, вы можете создать вместо этого универсальный атрибут:
public class GenericAttribute<T> : Attribute { }
Затем укажите параметр типа для использования атрибута:
[GenericAttribute<string>()]
public string Method() => default;
Комментарии:
1. Это C # 11 и все еще в предварительном просмотре: learn.microsoft.com/en-us/dotnet/csharp/whats-new /…