2013-05-07 7 views
11

Czy można oznaczyć właściwość w klasie bazowej za pomocą jakiegoś atrybutu, który będzie obowiązywał również w klasach potomnych?Kaskadowanie efektu atrybutu na przesłonięte właściwości w klasach podrzędnych

Pytanie może być bardzo specyficzne dla serializacji, ale zdecydowanie uważam, że mogą być również inne zastosowania.

Rozważmy następujący kod:

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); 
      } 
     } 
    } 
} 

Above zwrotów kod:

------ 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 

Myślałem IsValid_C1 będą ignorowane, chociaż tak nie jest. Czy istnieje sposób na osiągnięcie tego innego niż oznaczenie nieruchomości jako chronionej?

Edytuj: Szybki kod pokazujący, że dziedziczy się XmlIgnore adibut. http://ideone.com/HH41TE

+2

Jeśli "XmlIgnoreAttribute" zostało zadeklarowane z 'Inherited = true', powinno to działać. Niestety, tak nie jest i nie jest to sposób, aby to zadziałało: http://ideone.com/B0KASa '[AttributeUsageAttribute (AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue, Inherited = true) ] public class XmlInheritIgnoreAttribute: System.Xml.Serialization.XmlIgnoreAttribute { } ' –

+0

@ TIMS. Wypróbowałem twój kod i jak słusznie zasugerowałeś, to nie działało w tej sytuacji prawdopodobnie dlatego, że nie jest do tego przeznaczone? – Abhinav

+0

@hhinav Wystarczająco fair, nie zdawałem sobie sprawy. –

Odpowiedz

1

Nie sądzę, że istnieje sposób na dziedziczenie atrybutu, ponieważ przesłonić własność klasy podstawowej. trzeba by ozdobić IsValid_C1 C2 z 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; 
     } 
    } 
+3

Dopóki atrybut nie został oznaczony jako * un * 'Inheritable', jest automatycznie kaskadowany do klas pochodnych. Kiedy trochę się zastanawiam nad polem IsValid_C1, choć trochę inaczej, widzę, że jest on oznaczony 'XmlIgnoreAttribute'. Wierzę, że XmlSerializer nie wygląda tak samo jak ja. Zgadzam się, że powyższy kod jest jednym z rozwiązań, jeśli chcę, aby działał z 'XmlSerializer'. – Abhinav

+0

Nie wiedziałem tego i dobrze zrobiłem, żeby odzwierciedlić kod do sprawdzenia, dziękuję. +1 do Ciebie. :) –

1

złożę inny pogląd na to pytanie. Być może właśnie użyłeś tych właściwości jako przykładu i chcesz mieć właściwości kaskadowe. Ale myślę, że to dobry moment, aby pomyśleć o proponowanym modelu dziedziczenia. Zasadniczo możesz użyć zwykłego dziedziczenia lub pomyśleć o pewnym Wzorcu projektu, który nie może rozwiązać problemu związanego z serializacją, ale może zaoferować ci jeszcze więcej "luźnego sprzężenia" w twojej aplikacji, czyniąc go modelem bardziej składowym i pozwalają każdej klasie zajmować się tylko tym, co jest problemem, w ten sposób można ponownie wykorzystać wiele rzeczy i ułatwić sobie życie.

W oparciu o to myślenie przedstawiamy próbkę wzoru dekoracyjnego Decorator połączonego ze Wzorem Strategii. Gdybym był rozwój klasy jak te na próbkę, to jak chciałbym to zrobić:

/// <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; 
      } 
     } 
    } 

Następnie trzeba wdrożyć kilka klas, które mają logikę Wzór strategii, tak:

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; } 
    } 

teraz, gdy mamy logikę oddzielony od klasy, możemy odziedziczyć od dekoratorów, wybierając strategię Wich używają pola isValid, tak:

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; } 
    } 

jest to kod t O pokazują, że rozwiązanie działa:

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(); 
    } 

Wynikiem jest:

{<?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>} 

Więc może to nie odpowiadać jak kaskady atrybut w tym przypadku (bo można łatwo zrobić poprzez tworzenie własny atrybut (oznaczenie umożliwiające dziedziczenie), a następnie implementacja kodu do SerializeXML). Jest to kolejna opcja, która może poprawić ogólną architekturę rozwiązań wykorzystujących wzór projektu.Ale to rozwiązuje również ten problem :)

1

Wymagane zachowanie można osiągnąć za pomocą klas XmlAttributeOverrides i XmlAttributes. Pisałem metody pomocnika do tworzenia 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); 
    } 

Metoda Main stała:

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); 
     } 
    } 
0

Wydaje funkcja ta jest podzielona w języku C#.

Możesz napisać atrybut, który dzięki refleksji obniży twoje atrybuty. Całkiem prosto, jeśli rozumiesz odbicie.

Powiązane problemy