C# 如何将类内部的默认值设置为另一个类的属性?

C# 如何将类内部的默认值设置为另一个类的属性?,c#,default-value,C#,Default Value,我需要在另一个将第一个类定义为属性的类中设置一个属性。我想在子类中设置一个默认值。这方面的一个例子是: public enum NamingConvention { Name1 = 1, Name2 } public class Class1 { public Class1() { } public int Id { get; set; } public NamingConvention Naming{ get; set; } } p

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

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; }
}
公共枚举命名约定
{
名称1=1,
姓名2
}
公共班级1
{
公共类别1()
{
}
公共int Id{get;set;}
公共命名约定命名{get;set;}
}
公共课2
{
公共类别2()
{
}
公共列表名称1{get;set;}
}
公共班级3
{
公共类别2()
{
}
公共列表名称2{get;set;}
}
我希望能够在Class2和Class3中的Class1属性上放置一个属性或其他东西,以便在Class2中,命名属性设置为Name1,而对于Class3,它将自动设置为Name2


希望这是有道理的。我试着让这个例子尽可能简单。有什么想法吗?我试图避免使用抽象类,因为我的真实实体与nHibernate绑定,此时不想更改模型。

我会使用构造函数

在Class2的构造函数中:

public Class2()
{
    Name1Class = new Class1()
    Name1Class.Naming = NamingConvention.Name1
}
    public Class3()
    {
      Name2Class = new Class1()
      Name2Class.Naming = NamingConvention.Name2
    }
在Class3的构造函数中:

public Class2()
{
    Name1Class = new Class1()
    Name1Class.Naming = NamingConvention.Name1
}
    public Class3()
    {
      Name2Class = new Class1()
      Name2Class.Naming = NamingConvention.Name2
    }
如果你想变得有趣,你可以在Class1中的构造函数上设置一个参数,以便在创建对象时设置命名。

这可以通过使用自定义和反射来实现。这似乎不太可能比您目前所做的更好,但我将留给您进行评估

将TypeConverter属性应用于类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
}
定义类型转换器。注意:此简单转换器仅设置
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);
            }
        }
    }
} 
验证有效性的单元测试

[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));
}
您会注意到,这将清除
Class1的
Id
属性。如果不需要,您可以提出一个更具针对性的
SetDefaults
版本,该版本只重写
Class1
的特定属性。在这一点上,我不知道我是否真的会继续使用
DefaultValue
,因为用例与原始用例不同,将其与上述方法结合使用会产生意外的结果。为此,我可能会编写一个自定义的“DefaultNaminingConventionAttribute”

现在提供了一个具有列表属性的类

public class Class4
{
    public int Z { get; set; }
    [DefaultValue(typeof (Class1), "Name4")]
    public List<Class1> Name4Classes { get; set; }
}
公共类4
{
公共int Z{get;set;}
[DefaultValue(typeof(Class1),“Name4”)]
公共列表名称4类{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);
}
[测试]
public void SetListDefaultsHouldResetNamingConventionofEachListMember()的
{
var c4=新的类别4
{
Z=100,
Name4Classes=新列表
{
新的Class1{Id=1,Naming=NamingConvention.Name1},
新类别1{Id=2,Naming=NamingConvention.Name2},
新类别1{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)为.False);
Assert.That(c4.name4class.Any(c=>c.Naming==NamingConvention.Name4)为.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)为.False);
Assert.That(c4.Name4Classes.All(c=>c.Naming==NamingConvention.Name4)为.True);
}

最直接的方法是在类的构造函数中设置值。如果更改
Class2.Name1Class
值,
Class3.Name2Class
值也应该更改,这是您想要的吗?现在还不清楚,我们的
Class3
构造函数是否正确。主要目的是因为我们的系统有两个不同的机构表,我们正在将它们合并到一个列表中。然而,在某些地方,机构的名称需要以一种方式命名,而在另一种情况下,必须以另一种名称命名。我们通过nHibernate获取机构列表,因此我正在寻找一种简单的方法,以便在基于实例化机构的父类加载机构后设置正确的名称。更新了我的答案以合并对问题的更改。没有名称为Name1Class和Name2Class的类。。这些属性不是classesRight,应该都是Class1,我的错。我的问题是,我们填写了nHibernate的机构列表,所以我需要在加载属性后设置此值。在我们的实际代码中,它是一个机构列表。现在,根据我加载机构的位置,我循环遍历每一个机构并设置命名约定(参见上面问题中的注释)。我希望我可以附加一个属性,在加载数据后设置命名约定,这样我就不必在集合中循环并手动设置。只是想保存一些代码。谢谢您的回答。在我发布之前,我没有看到从
Class1
List
的更改。。。我会相应地更新。我假设对于列表,您只想设置Class1的命名属性,而不是所有ID?
[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);
}