#c# #serialization
#c# #сериализация
Вопрос:
Я пытаюсь написать некоторую инфраструктуру для облегчения обновления объектов между сервером и клиентом (ами). Вероятно, это будет использоваться в игре, однако я чувствую, что вопрос совсем не специфичен для игры (поэтому я задал его здесь).
По соображениям безопасности и эффективности я бы хотел, чтобы сервер выборочно обновлял свойства объекта. Например, определенное свойство объекта может быть полезным только для клиента, который управляет этим объектом, поэтому сервер обновит эту информацию только для «владельца». В качестве альтернативы, некоторые свойства, возможно, потребуется отправить всем клиентам. Для реализации этого я определил пользовательский атрибут, который определяет способ, которым сеть должна обрабатывать свойство:
[AttributeUsage(AttributeTargets.Property)]
public class NetworkParameterAttribute : System.Attribute
{
public enum NetworkParameterType
{
ServerToOwner,
ServerToAll,
ServerToOwnerView,
OwnerToServer
}
private NetworkParameterType type;
public NetworkParameterType Type
{
get
{
return type;
}
}
public NetworkParameterAttribute(NetworkParameterType Type)
{
this.type = Type;
}
}
Теперь в классе объектов я могу определять свойства следующим образом:
public class TestObject
{
[NetworkParameter(NetworkParameterAttribute.NetworkParameterType.ServerToAll)]
public int ID { get; set; }
[NetworkParameter(NetworkParameterAttribute.NetworkParameterType.ServerToOwner)]
public string Name { get; set; }
}
Затем я могу написать простую функцию, которая автоматически извлекает определенный набор свойств из объекта:
public byte[] GetBytes(NetworkParameterAttribute.NetworkParameterType type)
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
foreach (PropertyInfo info in this.GetType().GetProperties())
{
foreach (object attribute in info.GetCustomAttributes(true))
{
if (attribute is NetworkParameterAttribute amp;amp;
((NetworkParameterAttribute)attribute).Type == type)
{
formatter.Serialize(stream, info.GetValue(this, null));
}
}
}
byte[] buf = new byte[stream.Length];
Array.Copy(stream.GetBuffer(), buf, stream.Length);
return buf;
}
Аналогичная функция может собрать объект обратно на принимающей стороне. Проблема, с которой я сталкиваюсь, заключается в том, что сериализация очень неэффективна с точки зрения используемого пространства. Например, получение свойств ServerToAll из TestObject приводит к 54 байтам (тогда как это может быть всего лишь 4).
Итак, вопрос: существует ли более эффективный способ сериализации объектов в поток байтов, который будет работать для моей намеченной цели? Обратите внимание, что я бы предпочел не писать много кода, связанного с сериализацией.
Спасибо!
Ответ №1:
NetworkParameterAttribute должен иметь дополнительное поле, которое обозначает соответствующее свойство как «грязное». Каждое изменение свойства должно эффективно устанавливать этот флаг, и во время сериализации флаг должен быть сброшен. На самом деле сериализовать следует только грязные свойства.
Кроме того, теперь, когда объект сериализован только частично, во время сериализации теперь вам нужно предоставить информацию о том, какие свойства сериализуются. При заполнении потока сохраняйте один битовый вектор с грязными свойствами и помещайте этот битовый вектор в начало возвращаемого массива байтов.
РЕДАКТИРОВАТЬ: Вместо того, чтобы иметь флаг внутри атрибута, мы можем иметь фактическое значение, которое было сериализовано последним. Преимущество в том, что нам не нужен дополнительный код для каждого свойства, чтобы поддерживать синхронизацию флага со свойством. Во время сериализации мы сравниваем два значения и сериализуем свойство, если значения не равны.
Комментарии:
1. Мне нравится эта идея, и я определенно хотел реализовать ее наряду с тем, что я уже сделал. Есть ли способ автоматически устанавливать это свойство при изменении свойства?
2. Я не думаю, что такой механизм существует. Вам нужно было бы добавить код. Но вместо того, чтобы иметь флаг, мы могли бы дублировать данные. Я расширю ответ.