我有一个有近20个公共财产的班级。这些属性的共同之处在于它们都是字符串,并且它们填充了来自数据库的不同表的数据。
此外,该设置非常正常,而get是特殊的,因为我需要调用特定的方法。目前为每个房产做了这件事(见下文)。
我的问题是:是否有另一种更有效的方法,这样我就不必用这种方式手工定义每个公共财产?
class myclass
{
private string _Firstname;
private string _Lastname;
.....
public string Firstname
{
get {
return ModifyStringMethod(this._Firstname);
}
set {
this._Firstname = value;
}
}
}
如上所述,每个公共财产看起来都一样。 get调用ModifyStringMethod,私有成员作为参数给出,而set只设置私有成员。
答案 0 :(得分:18)
您可以尝试使用T4 template自动生成代码。当你拥有简单,重复的代码模式时,它们是完美的,你并不期望某些情况与其他情况略有不同。
只需使用属性名称列表定义XML,并让T4模板为每个属性生成部分类。
答案 1 :(得分:12)
另一个选项类似于Dion V.的解决方案,但它利用隐式转换使属性从外部表现得像普通字符串,并且可以使用简单的自动属性。但这仅在ModifyStringMethod
是静态且不需要来自类外部的参数时才有效。
public struct EllipsisString
{
private string _value;
public string Value {get { return _value; }}
public EllipsisString(string value)
{
_value = value;
}
// implicit conversion from string so it is possible to just assign string to the property
public static implicit operator EllipsisString(string value)
{
return new EllipsisString(value);
}
public static implicit operator string(EllipsisString original)
{
return SpecialMethod(original.Value);
}
public override string ToString()
{
return SpecialMethod(Value);
}
private static string SpecialMethod(string value)
{
return value + "...";
}
}
用法很简单:
private EllipsisString FirstName { get; set; }
public void Method()
{
FirstName = "Thomas";
Console.WriteLine(FirstName);
Console.WriteLine(FirstName.Value);
}
答案 2 :(得分:6)
PostSharp是另一种选择。
您只需在类上应用一个属性,并使用“get; set;”编写属性。语法。
PostSharp是一个.NET工具,它使开发人员能够将要执行的代码的各个方面应用于组件,命名空间,类或方法。
具体来说,PostSharp允许开发人员通过将属性应用于代码块来编写更少的代码,这些代码块稍后将反映该方面的代码被选中,并在所选代码块中执行。这种方法显着减少了代码库中冗余的“管道”。
常见用例包括以下内容:
登录
安全
撤消/重做
INotifyPropertyChanged的
ExceptionHandling
答案 3 :(得分:4)
在我过去遇到过这种情况时,我在VS中使用了自定义代码段来轻松创建属性。请参阅链接HERE。
然后,当您必须添加新属性时,只需调用代码段并根据需要填写项目的名称。
虽然这并不一定否定需要许多类似的属性, 它确实使它们的创建更容易(与使用上面提到的T4模板一样)。
答案 4 :(得分:4)
如果您可以将属性设置为虚拟,则可以使用带有Castle Dynamic Proxy的拦截器。
拦截器包含在调用给定方法时可以执行的行为。在这种情况下,我们将ModifyStringMethod转换为字符串属性的返回值。
如何强>
1)添加对nuget包Castle.Core的引用
2)定义你的拦截器
public class ModifyStringMethodInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
invocation.Proceed();
if (invocation.Method.Name.StartsWith("get_") &&
invocation.Method.ReturnType == typeof(string))
{
invocation.ReturnValue =
ModifyStringMethod((string)invocation.ReturnValue);
}
}
private static string ModifyStringMethod(string input)
{
return (input ?? "") + "MODIFIED";
}
}
上面的示例有一个Intercept方法,在调用属性时将调用该方法。您可以在示例中看到 invocation.Proceed(),这将继续调用该属性。
然后它检查它是否是一个get_属性并返回一个字符串
if (invocation.Method.Name.StartsWith("get_") &&
invocation.Method.ReturnType == typeof(string))
然后修改方法的返回值。
invocation.ReturnValue = ModifyStringMethod((string)invocation.ReturnValue);
3)使用虚拟方法定义要添加此行为的对象(注意我也可以在此处使用自动实现的属性)BONUS
public class Intercepted
{
public virtual string A { get; set; }
}
4)然后使用DynamicProxy中的ProxyGenerator类
创建对象实例e.g
public class Program
{
static void Main(string[] args)
{
var pg = new ProxyGenerator();
// Intercepted will be an instance of Intercepted class with the
// ModifyStringMethodInterceptor applied to it
var intercepted = pg.CreateClassProxy<Intercepted>(new ModifyStringMethodInterceptor());
intercepted.A = "Set ... ";
Console.WriteLine(intercepted.A);
Console.ReadLine();
}
}
输出
Set ... MODIFIED
这里的好处是你的对象是&#34;清洁&#34;例如,他们不需要知道ModifyStringMethodInterceptor,并且可以包含自动实现的属性,如果你有足够的这些对象,将大量减少代码量。
如果您需要进一步控制,可以更进一步,可以通过向类添加属性来应用此行为,例如
[AttributeUsage(AttributeTargets.Method)]
public class ModifyStringMethodAttribute : Attribute
{
}
然后将对象定义为:
public class Intercepted
{
public virtual string A { [ModifyStringMethod] get; set; }
}
对拦截器的改变:
if (invocation.Method.ReturnType == typeof(string) &&
invocation.Method.GetCustomAttributes(true)
.OfType<ModifyStringMethodAttribute>().Any())
{
invocation.ReturnValue =
ModifyStringMethod((string)invocation.ReturnValue);
}
检查属性,然后应用方法调用。
答案 5 :(得分:3)
但是,我个人不是这个解决方案的粉丝,你可以这样做:
class MyClass
{
private IDictionary<string, string> propertyValueByName = new Dictionary<string, string>();
public string this[string propertyName]
{
get { return propertyValueByName[propertyName]; }
set { propertyValueByName[propertyName] = ModifyStringMethod(value); }
}
public string FirstName
{
get { return this["FirstName"]; }
set { this["FirstName"] = value; }
}
public string LastName
{
get { return this["LastName"]; }
set { this["LastName"] = value; }
}
}
答案 6 :(得分:3)
使用反射
的示例class MyClass
{
public string FirstName { private get; set; }
public string LastName { private get; set; }
public string GetModifiedValue(string propertyName)
{
var prop = this.GetType().GetProperty(propertyName);
return ModifyStringMethod((string)prop.GetValue(this, null));
}
}
为了获得每个修改后的值而不是MyClass.FirstName
,您可以使用MyClass.GetModifiedValue("FirstName")
答案 7 :(得分:3)
您可以在此处创建自定义代码段,这是我为自己创建的剪辑示例,可以使用更改通知自动创建属性,您可以将其用作模板:
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>propnot</Title>
<Shortcut>propnot</Shortcut>
<Description>Code snippet for property and backing field with property change event</Description>
<Author>Radin Gospodinov</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>type</ID>
<ToolTip>Property type</ToolTip>
<Default>int</Default>
</Literal>
<Literal>
<ID>property</ID>
<ToolTip>Property name</ToolTip>
<Default>MyProperty</Default>
</Literal>
<Literal>
<ID>field</ID>
<ToolTip>The variable backing this property</ToolTip>
<Default>myVar</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[private $type$ $field$;
public $type$ $property$
{
get { return this.$field$;}
set {
if(this.$field$ != value) {
$field$ = value;
this.RaisePropertyChanged(() => this.$property$);
}
}
}
$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
答案 8 :(得分:3)
如果你真的想要采用这种方法,那么使用像T4模板或CodeSmith这样的代码生成可能是要走的路,但我同意@DanBryant建立这样的属性会导致反直觉类。我希望这样的代码可以工作:
X.FirstName = "Some random long name";
Assert.AreEqual("Some random long name", X.FirstName);
根据您的评论,您的班级设计可能不起作用(取决于ModifyStringMethod
中的截断长度,您可能实际得到X.FireName == "Some rand..."
。这似乎不对。
更好的方法可能是在扩展方法中实现属性行为之外的修改。所以像这样:
public static class FormatStringExtensions {
public static string ModifyStringForOutput(this string me) {
if (me.Length > 10) {
return me.Substring(0, 10) + "...";
}
return me;
}
};
允许您使用自动属性定义数据类:
public class myclass {
public string FirstName { get; set; }
public string LastName {get; set; }
};
然后使用扩展方法修改属性中的字符串值:
var instance = new myclass();
instance.FirstName = "01234567890123";
Console.WriteLine("Original Name {0}\nModified Name {1}\n",
instance.FirstName,
instance.FirstName.ModifyStringForOutput());
这使您的属性可以像预期的那样继续工作,同时如果需要,可以轻松访问格式化的字符串。
答案 9 :(得分:2)
替代方法是使用类型和属性名称作为变量创建一个简单的代码段。生成课程要快得多,并且您可以完全控制代码。
答案 10 :(得分:2)
您可以定义要从DynamicObject
继承的自定义类public class MyExpando : DynamicObject
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
//Want to create properties on initialization? Do it in the constructor
public MyExpando()
{
dictionary.Add("PreferredName", "Darth Sidious");
dictionary.Add("GreatDialog", "Something, something, darkside!");
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
bool success = dictionary.TryGetValue(binder.Name, out result);
if (success)
result = ModifiedValue(result);
return success;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
private string ModifiedValue(object val)
{
//Modify your string here.
if (val.ToString() != "Darth Sidious")
return "Something something complete";
return val.ToString();
}
}
想要创建不在构造函数中的属性?那你就可以做到
dynamic x = new MyExpando();
x.FirstName = "Sheev";
x.LastName = "Palpatine"
Console.WriteLine(x.PreferredName + " says : \"" + x.GreatDialog + "\"");
Console.ReadKey();
关于这一点很棒的是你可以实现INotifyPropertyChanged,然后在TrySetMember
方法中调用它,你也可以限制访问你的&#34;属性&#34; setter或getter只需根据TryGetMember或TrySetMember中的属性名称抛出异常,只需为从MyExpando
继承的类更改字典的访问修饰符,以模拟属性继承。
如何限制对属性设置器的访问的示例。
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (!dictionary.ContainsKey(binder.Name))
return false;
dictionary[binder.Name] = value;
return true;
}