簡體   English   中英

如何將類中的默認值設置為另一個類的屬性?

[英]How can I set a default value inside of a class as a property of another class?

我需要在另一個類中設置一個類的屬性,該類將第一個類定義為屬性。 我想在子類中默認一個值。 一個例子是:

public enum NamingConvention
{
    Name1 = 1,
    Name2
}
public class Class1
{
    public Class1()
    {
    }

    public int Id { get; set; }
    public  NamingConvention Naming{ get; set; }
}

public class Class2
{
    public Class2()
    {
    }

    public List<Class1> Name1s { get; set; }
}

public class Class3
{
    public Class2()
    {
    }

    public List<Class1> Name2s { get; set; }
}

我希望能夠在Class2和Class3中的Class1屬性上放置一個屬性或類似內容,以便在Class2中,Naming屬性設置為Name1,而對於Class3,它將自動設置為Name2。

希望有道理。 我試着盡可能簡單地做一個例子。 有什么想法嗎? 我試圖避免抽象類,因為我的真實實體與nHibernate綁定,並且不想在此時更改模型。

我會使用構造函數。

在Class2的構造函數中:

public Class2()
{
    Name1Class = new Class1()
    Name1Class.Naming = NamingConvention.Name1
}

在Class3的構造函數中:

    public Class3()
    {
      Name2Class = new Class1()
      Name2Class.Naming = NamingConvention.Name2
    }

如果你想得到想象,你可以在Class1的構造函數上放置一個參數,以允許你在創建對象時設置Naming。

可以通過使用DefaultValueAttribute ,自定義TypeConverter和Reflection來完成。 這似乎不太可能比你目前正在做的更好,但我會留給你評價。

將TypeConverter屬性應用於Class 1

[TypeConverter(typeof(Class1Converter))]
public class Class1
{
    public int Id { get; set; }
    public NamingConvention Naming { get; set; }
}
public enum NamingConvention
{
    Name1 = 1,
    Name2,
    Name3,
    Name4
}

定義Class1Converter。 請注意,此簡單轉換器僅設置NamingConvention參數的值。

public class Class1Converter: TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, 
                                        Type sourceType)
    {
        if (sourceType == typeof(string))
        {
            return true;
        }
        return base.CanConvertFrom(context, sourceType);
    }
    public override bool CanConvertTo(ITypeDescriptorContext context,
                                      Type destinationType)
    {
        if(destinationType == typeof(Class1))
        {
            return true;
        }
        return base.CanConvertTo(context, destinationType);
    }
    public override object ConvertFrom(ITypeDescriptorContext context,
                                       System.Globalization.CultureInfo culture, 
                                       object value)
    {
        var stringValue = value as string;
        if(stringValue != null)
        {
                            return new Class1
                {
                    Naming = (NamingConvention)Enum.Parse(typeof(NamingConvention), stringValue)
                };
        }
        return base.ConvertFrom(context, culture, value);
    }
}

為方便起見,我在擴展方法中聲明了這一點,它可以很容易地設置為默認類的一部分......

public static class DefaultExtension
{
    public static IEnumerable<PropertyInfo> GetProperties<T>(this Type type)
    {
        return type.GetProperties().Where(p => p.PropertyType == typeof (T));
    }
    public static void SetDefaults<T>(this T toDefault)
    {
        foreach (PropertyInfo p in toDefault.GetType().GetProperties())
        {
            foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
            {
                p.SetValue(toDefault, dv.Value, null);
            }
        }
    }
} 

最后,在屬性上聲明DefaultValue屬性。 為了方便起見,我在這里從構造函數調用SetDefaults() ,在你的情況下,你仍然需要在從NHibernate加載實例后調用它。

public class Class2
{
    public int X { get; set; }
    [DefaultValue(typeof(Class1), "Name2")]
    public Class1 Name2Class { get; set; }
    public Class2()
    {
        this.SetDefaults();
    }
}

public class Class3
{
    public int Y { get; set; }
    [DefaultValue(typeof(Class1), "Name3")]
    public Class1 Name3Class { get; set; }
        public Class3()
        {
            this.SetDefaults();
        }
}

單元測試證明有效性......

[Test]
public void TestDefaultValueAttribute()
{
    //Class2 have Name2 as the default value for the Naming property
    var c2 = new Class2();
    Assert.That(c2,Is.Not.Null);
    Assert.That(c2.Name2Class, Is.Not.Null);
    Assert.That(c2.Name2Class.Naming, Is.EqualTo(NamingConvention.Name2));
    //Class3 have Name3 as the default value for the Naming Property
    var c3 = new Class3();
    Assert.That(c3, Is.Not.Null);
    Assert.That(c3.Name3Class, Is.Not.Null);
    Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
    //wipes out other properties of the Class1 attribute.
    // to demonstrate, set properties to something other than the default then call
    // SetDefaults again.

    c3.Name3Class.Naming = NamingConvention.Name1;
    c3.Name3Class.Id = 10;
    c3.SetDefaults();
    Assert.That(c3.Name3Class.Id, Is.EqualTo(0));
    Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
}

你會注意到這會消除Class1Id屬性。如果不需要,你可以想出一個更有針對性的SetDefaults版本,它只覆蓋了Class1特定屬性。 此時我不知道我是否真的會繼續使用DefaultValue ,因為用例與原始版本不同,並且與上述方法結合使用會產生意外結果。 我可能會為此目的編寫一個自定義的'DefaultNaminingConventionAttribute。

public static void SetDefaultNamingConvention<T>(this T toDefault)
{
    foreach (PropertyInfo p in toDefault.GetType().GetProperties<Class1>())
    {
        foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
        {
            var pValue = p.GetValue(toDefault, null) as Class1;
            if (pValue != null)
            {
                pValue.Naming = ((Class1)dv.Value).Naming;
            }
            else
            {
                p.SetValue(toDefault, dv.Value, null);
            }
        }
    }
}

[Test]
public void SetDefaultNamingConventionDefaultShouldOnlyDefaultNamingProperty()
{
    var c3 = new Class3();
    c3.Name3Class.Naming = NamingConvention.Name1;
    c3.Name3Class.Id = 20;
    c3.SetDefaultNamingConvention();
    Assert.That(c3.Name3Class.Id, Is.EqualTo(20));
    Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
}

編輯:更新以處理列表成員的默認設置
使用這個新的SetListDefaults擴展方法,我們現在可以將默認值應用於List<Class1>成員。 在這里,我幾乎肯定不再使用DefaultValue ,而是定義一個用於集合的自定義屬性。 但這超出了問題的范圍。

public static class DefaultExtension
{
    public static IEnumerable<PropertyInfo> GetProperties<T>(this Type type)
    {
        return type.GetProperties().Where(p => p.PropertyType == typeof (T));
    }
   public static void SetListDefaults<T>(this T toDefault)
    {
        foreach (PropertyInfo p in toDefault.GetType().GetProperties<List<Class1>>())
        {
            foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
            {
                var pValue = p.GetValue(toDefault, null) as List<Class1>;
                if (pValue != null)
                {
                    foreach (var class1 in pValue)
                    {
                        class1.Naming = ((Class1) dv.Value).Naming;
                    }
                }
            }
        }
    }
}

現在提供了一個帶有List屬性的類......

public class Class4
{
    public int Z { get; set; }
    [DefaultValue(typeof (Class1), "Name4")]
    public List<Class1> Name4Classes { get; set; }
}

並且僅修改用於驗證列表中每個項目的命名屬性的單元測試。

[Test]
public void SetListDefaultsShouldResetNamingConventionOfEachListMember()
{
    var c4 = new Class4
        {
            Z = 100,
            Name4Classes = new List<Class1>
                {
                    new Class1 {Id = 1, Naming = NamingConvention.Name1},
                    new Class1 {Id = 2, Naming = NamingConvention.Name2},
                    new Class1 {Id = 3, Naming = NamingConvention.Name3}
                }
        };
    Assert.That(c4.Name4Classes, Is.Not.Empty);
    Assert.That(c4.Name4Classes.Count, Is.EqualTo(3));
    Assert.That(c4.Name4Classes.Any(c => c.Id == 0), Is.False);
    Assert.That(c4.Name4Classes.Any(c => c.Naming == NamingConvention.Name4), Is.False);
    c4.SetListDefaults();
    Assert.That(c4.Name4Classes, Is.Not.Empty);
    Assert.That(c4.Name4Classes.Count, Is.EqualTo(3));
    Assert.That(c4.Name4Classes.Any(c=> c.Id == 0), Is.False);
    Assert.That(c4.Name4Classes.All(c=> c.Naming == NamingConvention.Name4), Is.True);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM