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