Каскадирование влияния атрибута на переопределенные свойства в дочерних классах
Можно ли пометить свойство в базовом классе некоторым атрибутом, который также остается эффективным в дочерних классах?
Вопрос может быть очень специфичным для Serialization, но я определенно думаю, что могут быть и другие применения.
Рассмотрим следующий код:
using System;
using System.IO;
using System.Xml.Serialization;
namespace Code.Without.IDE
{
[Serializable]
public abstract class C1
{
[XmlIgnore]
public abstract bool IsValid_C1 { get; set;}
}
[Serializable]
public class C2 : C1
{
public bool IsValid_C2 { get; set; }
public override bool IsValid_C1 { get; set;}
public C2()
{
IsValid_C1 = true;
IsValid_C2 = false;
}
}
public static class AbstractPropertiesAttributeTest
{
public static void Main(string[] args)
{
C2 c2 = new C2();
using(MemoryStream ms = new MemoryStream())
{
XmlSerializer ser = new XmlSerializer(typeof(C2));
ser.Serialize(ms, c2);
string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine(result);
}
}
}
}
Выше кода возвращается:
------ C:\abhi\Code\CSharp\without IDE\AbstractPropertiesAttributeTest.exe
<?xml version="1.0"?>
<C2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<IsValid_C2>false</IsValid_C2>
<IsValid_C1>true</IsValid_C1>
</C2>
------ Process returned 0
Я думал, что IsValid_C1
будет проигнорирован, хотя это не так. Есть ли способ достичь этого, кроме обозначения свойства как защищенного?
Изменить: быстрый код, показывающий, что атрибут XmlIgnore
наследуется.
http://ideone.com/HH41TE
Ответы
Ответ 1
Я не верю, что существует способ наследования атрибута, поскольку вы переопределяете свойство базового класса. Вам нужно будет украсить IsValid_C1 из C2 с помощью XmlIgnore:
[Serializable]
public class C2 : C1
{
public bool IsValid_C2 { get; set; }
[XmlIgnore]
public override bool IsValid_C1 { get; set; }
public C2()
{
IsValid_C1 = true;
IsValid_C2 = false;
}
}
Ответ 2
Я предлагаю другое мнение по этому вопросу. Возможно, вы просто использовали эти свойства в качестве примера и хотите, чтобы у вас было несколько каскадных свойств. Но я думаю, что это может быть подходящее время, чтобы подумать о предложенной модели наследования.
В основном вы можете использовать регулярное наследование или подумать о некотором шаблоне проектирования, который не может просто решить проблему, связанную с сериализацией, но может предложить вам еще более "свободное соединение" в вашем приложении, сделав его более модельным компонентом и позволяя каждому классу заниматься только тем, что вызывает беспокойство, таким образом вы можете повторно использовать множество вещей и облегчить свою жизнь.
Основываясь на этом мышлении, я предоставляю вам образец шаблона проектирования Decorator, смешанный с шаблоном проектирования стратегии. Если бы я разрабатывал классы, подобные тем, которые были на вашем образце, я бы это сделал:
/// <summary>
/// The interface for validation strategy (since we are using interface, there is no need for another abstract class)
/// </summary>
public interface IValidation
{
bool IsValid { get; set; }
}
/// <summary>
/// The decorator (it dont need to be abstract) that has the serializable properties
/// </summary>
[Serializable]
public class ValidatableDecorator : IValidation
{
protected IValidation instance;
public ValidatableDecorator()
{
Init();
}
public ValidatableDecorator(IValidation instance)
{
Init();
}
protected virtual void Init() { }
public void Set(IValidation instance)
{
this.instance = instance;
}
[XmlIgnore]
public bool IsValid
{
get
{
return instance.IsValid;
}
set
{
instance.IsValid = value;
}
}
}
Затем вам нужно реализовать некоторые классы, которые имеют логику шаблона стратегии, например:
public class BossValidatorImplementation : IValidation
{
public bool IsValid
{
get
{
return false; ;
}
set
{
throw new InvalidOperationException("I dont allow you to tell me this!");
}
}
}
public class EasyGoingValidator : IValidation
{
public bool IsValid { get; set; }
}
Теперь, когда у нас есть логика, отделенная от класса, мы можем наследовать от декораторов, выбирая стратегию, которую они используют для поля IsValid, например:
public class ChildWithBossValidation : ValidatableDecorator
{
protected ChildWithBossValidation(IValidation instance)
: this()
{
Init();
}
public ChildWithBossValidation()
: base(new BossValidatorImplementation())
{
Init();
}
protected override void Init()
{
Name = "I'm the boss!";
Sallary = 10000d;
}
public string Name { get; set; }
public double Sallary { get; set; }
}
public class ChildWithEasyGoingValidation : ValidatableDecorator
{
public ChildWithEasyGoingValidation()
: base(new EasyGoingValidator())
{
}
protected ChildWithEasyGoingValidation(IValidation instance)
: this()
{
}
protected override void Init()
{
Name = "Do as you please... :) ";
}
public string Name { get; set; }
}
Это код, показывающий, что решение работает:
public static void Main(string[] args)
{
var boos = new ChildWithBossValidation();
var coolGuy = new ChildWithEasyGoingValidation();
using (var ms = new MemoryStream())
{
var ser = new XmlSerializer(boos.GetType());
ser.Serialize(ms, boos);
string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine("With override");
Console.WriteLine(result);
}
Console.WriteLine("-------------");
using (var ms = new MemoryStream())
{
var ser = new XmlSerializer(coolGuy.GetType());
ser.Serialize(ms, coolGuy);
string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine("With override");
Console.WriteLine(result);
}
Console.ReadKey();
}
Результат:
{<?xml version="1.0"?>
<ChildWithBossValidation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>I'm the boss!</Name>
<Sallary>10000</Sallary>
</ChildWithBossValidation>-------------------<?xml version="1.0"?>
<ChildWithEasyGoingValidation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Do as you please... :) </Name>
</ChildWithEasyGoingValidation>}
Итак, возможно, это не отвечает на то, как каскадировать атрибут в этом случае (потому что вы можете легко сделать это, создав свой собственный атрибут (маркировка для разрешения наследования), а затем внедряя некоторый код в SerializeXML). Это еще один вариант, который может улучшить общую архитектуру решений с использованием шаблона проектирования. Но это также решает эту проблему:)
Ответ 3
Необходимое поведение может быть достигнуто с помощью классов XmlAttributeOverrides и XmlAttributes. Я написал вспомогательный метод для создания XmlSerializer:
public static XmlSerializer GetXmlSerializerWithXmlIgnoreFields(Type t)
{
XmlAttributeOverrides xmlOverrides = new XmlAttributeOverrides();
foreach (var prop in t.GetProperties(BindingFlags.Public|BindingFlags.Instance))
{
Attribute xmlIgnoreAttribute = Attribute.GetCustomAttribute(prop, typeof(XmlIgnoreAttribute));
if (xmlIgnoreAttribute == null)
continue;
XmlAttributes xmlAttributes = new XmlAttributes();
xmlAttributes.XmlIgnore = true;
xmlOverrides.Add(t, prop.Name, xmlAttributes);
}
return new XmlSerializer(t, xmlOverrides);
}
Основной способ:
public static void Main(string[] args)
{
C2 c2 = new C2();
using (MemoryStream ms = new MemoryStream())
{
XmlSerializer ser = GetXmlSerializerWithXmlIgnoreFields(typeof(C2));
ser.Serialize(ms, c2);
string result = System.Text.Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine(result);
}
}
Ответ 4
Похоже, эта функция нарушена на С#.
Вы можете написать атрибут, который через отражение приведет к уменьшению атрибутов для вас. Довольно прямо, если вы понимаете отражение.