środa, 22 lutego 2012

Metody rozszerzające, Atrybuty i Refleksje w .NET [PL]

Czy korzystacie z refleksji w .NET? Czy zdarzyło wam się przeglądać/ustawiać właściwości klas poprzez refleksje? Czy korzystacie z atrybutów? Mnie często i równie często zdarzało mi się ponownie szukać kodu, który rozwiązałby mój problem, dlatego dla wygody poniżej zamieszczam różne funkcje rozszerzające, które realizują wspomniane wyżej zadania. (być może komuś jeszcze się one przydadzą).


Nie będę tutaj się zagłębiał w to, jak działają (chyba wszyscy to wiedzą :) ), skupmy się raczej na funkcjonalności:
  • GetAllPublicPropertiesAndValues – Funkcja ma za zadanie zwrócić słownik z parami: nazwa właściwości + wartość.
  • GetAllPublicPropertiesMarkedWithAttributeAndValues – podobnie jak poprzednia, ale tutaj wskazujemy atrybut, jakim muszą być oznaczone właściwości, by znalzały się w wynikowym słowniku.
  • GetValueBasedOnSampleAttributeName i SetValueBasedOnSampleAttributeName – pobiera lub ustawia wartość właściwości oznaczonej atrybutem o pewnej wskazanej nazwie.
  • GetValuesBasedOnSampleAttributeOrder – zwraca wartości publicznych właściwości w kolejności wskazanej przez atrybut.
  • SetValueByPropertyName – ustawia wartość właściwości poprzez wskazanie nnazwy wybranej właściwości.
  • UpdatePublicPropertiesBaseOnInstance – ustawia (aktualizuje) wartości wszystkich publicznych właściwości (dla których jest akcesor typu set), nowe wartości brane są z własciwości elementu źródłowego według odpowiadających sobie nazwą property.
Przykładowy kod dołączony do tego posta jest oparty o pewne założenia, które pozwoliły na pewne uproszczenia (m.in. nieuwzględnianie indeksów dla właściwości, brak obsługi błędów i przeniesienie jej na klasę wykorzystującą wspomniane metody, itp...)
Poniższy kod wymaga dodatkowo przykładowej klasy i przykładowego atrybutu:
  public class SampleAttribute:Attribute
  {
    public string Name { get; private set; }
    public int Order { get; set; }
    public SampleAttribute( string name )
    {
      Name = name;
    }
    public override string ToString()
    {
      return String.Format( "SampleAttribute: {0}, order={1}", Name, Order );
    }
  }
  public class SampleClass
  {
    [Sample("String")]
    public string StringValue { get; set; }
    [Sample( "Int", Order = 3 )]
    public int IntValue { get; set; }
    [Sample( "Double", Order = 2 )]
    public double DoubleValue { get; set; }
    public string ReadOnlyValue { get; private set; }
    public SampleClass()
    {
      ReadOnlyValue = "NoInitialisation";
    }
    public SampleClass( string s, int i, double d )
    {
      StringValue = s;
      IntValue = i;
      DoubleValue = d;
      ReadOnlyValue = "MyReadOnlyStringValue";
    }
  }
Kod wspomnianych metod rozszerzających, to:
  public static class ReflectionExtensions
  {
    public static Dictionary<string, object> GetAllPublicPropertiesAndValues( this object instance )
    {
      Dictionary<string, object> toBeReturned = new Dictionary<string, object>();
      foreach ( PropertyInfo property in instance.GetType().GetProperties() )
      {
        toBeReturned.Add( property.Name, property.GetValue( instance, null ) );
      }
      return toBeReturned;
    }

    public static Dictionary<string, object> GetAllPublicPropertiesMarkedWithAttributeAndValues( this object instance, Type typeAttribute )
    {
      Dictionary<string, object> toBeReturned = new Dictionary<string, object>();
      foreach ( PropertyInfo property in from p in instance.GetType().GetProperties()
                                         where ( p.GetCustomAttributes( typeAttribute, true ) ).Length > 0
                                         select p )
      {
        toBeReturned.Add( property.Name, property.GetValue( instance, null ) );
      }
      return toBeReturned;
    }

    public static object GetValueBasedOnSampleAttributeName( this object instance, string attributeName )
    {
      return GetPropertyInfoBasedOnSampleAttributeName( instance, attributeName ).GetValue( instance, null );
    }

    public static void SetValueBasedOnSampleAttributeName( this object instance, string attributeName, object val )
    {
      GetPropertyInfoBasedOnSampleAttributeName( instance, attributeName ).SetValue( instance, val, null );
    }

    public static object[] GetValuesBasedOnSampleAttributeOrder( this object instance )
    {
      var values = ( from p in instance.GetType().GetProperties()
                     where ( p.GetCustomAttributes( typeof( SampleAttribute ), true ) ).Length > 0
                     select p )
                     .OrderBy(
                       pr =>
                         ( (SampleAttribute)pr.GetCustomAttributes( typeof( SampleAttribute ), true )
                         .FirstOrDefault() ).Order )
                     .Select( pr => pr.GetValue( instance, null ) );
      return values.ToArray();
    }

    public static void SetValueByPropertyName( this object instance, string propertyName, object val )
    {
      ( from p in instance.GetType().GetProperties()
        where p.Name == propertyName
        select p )
        .FirstOrDefault()
        .SetValue( instance, val, null );
    }

    public static void UpdatePublicPropertiesBaseOnInstance( this object instance, object source )
    {
      foreach ( var instancePropertyInfo in ( from p in instance.GetType().GetProperties()
                                              where p.GetSetMethod() != null
                                              select p ) )
      {
        var sourcePropertyInfo = ( from p in instance.GetType().GetProperties()
                                   where p.Name == instancePropertyInfo.Name
                                   select p )
                                                .FirstOrDefault();
        if ( sourcePropertyInfo != null )
        {
          var valueToBeSet = sourcePropertyInfo.GetValue( source, null );
          instancePropertyInfo.SetValue( instance,
                                        valueToBeSet,
                                        null );
        }
      }
    }

    private static PropertyInfo GetPropertyInfoBasedOnSampleAttributeName( object instance, string attributeName )
    {
      PropertyInfo property = ( from p in instance.GetType().GetProperties()
                                where
                                  ( from a in p.GetCustomAttributes( typeof( SampleAttribute ), true )
                                    where ( (SampleAttribute)a ).Name == attributeName
                                    select a ).Any()
                                select p ).FirstOrDefault();
      return property;
    }
  }


Na koniec zamiast przykładów użycia, przykładowe testy :)
    [TestMethod]
    public void GetAllPublicPropertiesAndValues_Test()
    {
      var sampleInstance = new SampleClass( "value1", 77, 11.11 );
      var result = sampleInstance.GetAllPublicPropertiesAndValues();
      Assert.AreEqual( 4, result.Count );
      Assert.AreEqual( sampleInstance.StringValue, result[ "StringValue" ] );
      Assert.AreEqual( sampleInstance.IntValue, result[ "IntValue" ] );
      Assert.AreEqual( sampleInstance.DoubleValue, result[ "DoubleValue" ] );
      Assert.AreEqual( sampleInstance.ReadOnlyValue, result[ "ReadOnlyValue" ] );
    }
 
    [TestMethod]
    public void GetAllPublicPropertiesMarkedWithAttributeAndValues_Test()
    {
      var sampleInstance = new SampleClass( "value1", 77, 11.11 );
      var result = sampleInstance.GetAllPublicPropertiesMarkedWithAttributeAndValues(typeof(SampleAttribute));
      Assert.AreEqual( 3, result.Count );
      Assert.AreEqual( sampleInstance.StringValue, result[ "StringValue" ] );
      Assert.AreEqual( sampleInstance.IntValue, result[ "IntValue" ] );
      Assert.AreEqual( sampleInstance.DoubleValue, result[ "DoubleValue" ] );
    }

    [TestMethod]
    public void GetValueBasedOnSampleAttributeName_Test()
    {
      var sampleInstance = new SampleClass( "value1", 77, 11.11 );
      Assert.AreEqual( sampleInstance.StringValue, sampleInstance.GetValueBasedOnSampleAttributeName("String"));
      Assert.AreEqual( sampleInstance.IntValue, sampleInstance.GetValueBasedOnSampleAttributeName( "Int" ) );
      Assert.AreEqual( sampleInstance.DoubleValue, sampleInstance.GetValueBasedOnSampleAttributeName( "Double" ) );
    }

    [TestMethod]
    public void GetValuesBasedOnSampleAttributeOrder_Test()
    {
      var sampleInstance = new SampleClass( "value1", 77, 11.11 );
      var result = sampleInstance.GetValuesBasedOnSampleAttributeOrder( );
      Assert.AreEqual( 3, result.Length );
      Assert.AreEqual( sampleInstance.StringValue, result[ 0 ] );
      Assert.AreEqual( sampleInstance.IntValue, result[ 2 ] );
      Assert.AreEqual( sampleInstance.DoubleValue, result[ 1 ] );
    }

    [TestMethod]
    public void SetValuesBasedOnSampleAttributeName_Test()
    {
      var sampleInstance = new SampleClass();
      sampleInstance.SetValueBasedOnSampleAttributeName( "Int", 123 );
      sampleInstance.SetValueBasedOnSampleAttributeName( "Double", 99.456 );
      sampleInstance.SetValueBasedOnSampleAttributeName( "String", "SampleText" );
      Assert.AreEqual( sampleInstance.StringValue, "SampleText" );
      Assert.AreEqual( sampleInstance.IntValue, 123 );
      Assert.AreEqual( sampleInstance.DoubleValue, 99.456 );
    }

    [TestMethod]
    public void SetValueByPropertyName_Test()
    {
      var sampleInstance = new SampleClass();
      sampleInstance.SetValueByPropertyName( "IntValue", 123 );
      sampleInstance.SetValueByPropertyName( "DoubleValue", 99.456 );
      sampleInstance.SetValueByPropertyName( "StringValue", "SampleText" );
      Assert.AreEqual( sampleInstance.StringValue, "SampleText" );
      Assert.AreEqual( sampleInstance.IntValue, 123 );
      Assert.AreEqual( sampleInstance.DoubleValue, 99.456 );
    }

    [TestMethod]
    public void UpdatePublicPropertiesBaseOnInstance_Test()
    {
      var sampleSourceInstance = new SampleClass( "value1", 77, 11.11 );
      var instance = new SampleClass();
      instance.UpdatePublicPropertiesBaseOnInstance( sampleSourceInstance );
      Assert.AreEqual( "value1", instance.StringValue  );
      Assert.AreEqual( 77, instance.IntValue );
      Assert.AreEqual( 11.11, instance.DoubleValue  );
      Assert.AreEqual("NoInitialisation", instance.ReadOnlyValue  );

    }
Promuj

Brak komentarzy:

Prześlij komentarz

Posty powiązane / Related posts