如何将类中的默认值设置为另一个类的属性?

时间:2014-04-25 17:37:21

标签: c# default-value

我需要在另一个类中设置一个类的属性,该类将第一个类定义为属性。我想在子类中默认一个值。这方面的一个例子是:

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绑定,并且不想在此时更改模型。

2 个答案:

答案 0 :(得分:1)

我会使用构造函数。

在Class2的构造函数中:

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

在Class3的构造函数中:

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

如果你想得到想象,你可以在Class1的构造函数中放置一个参数,以允许你在创建对象时设置命名。

答案 1 :(得分:1)

可以使用DefaultValueAttribute,自定义TypeConverter和反射来完成 。这似乎不太可能比你现在做的更好,但我会留下你的评价。

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

您会注意到这会消除Id Class1的{​​{1}}属性。如果不需要,您可以提出更具针对性的SetDefaults版本,该版本仅覆盖了Class1的特定属性DefaultValue此时我不知道我是否真的会继续使用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)); } ,因为用例与原始版本不同,并且与上述方法结合使用会产生意外结果。我可能会为此目的编写一个自定义&#39; DefaultNaminingConventionAttribute。

List<Class1>

编辑:更新以处理列表成员的默认设置
使用这个新的SetListDefaults扩展方法,我们现在可以将默认值应用于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; } } } } } } ,而是定义一个用于集合的自定义属性。但这超出了问题的范围。

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

现在提供了一个带有List属性的类......

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

并且仅修改列表中每个项目的命名属性的单元测试。

{{1}}