我有一个类似这样的课程:
public class Abc
{
public string City
{
get { return _getValue(); }
set { _setValue(value); }
}
public string State
{
get { return _getValue(); }
set { _setValue(value); }
}
private string _getValue()
{
// determine return value via StackTrace and reflection
}
...
}
(是的,我知道StackTrace /反射很慢;不要让我感到震惊)
鉴于 ALL 属性被声明为相同,我能够做的就是有一些简单/干净的方式来声明它们而不需要复制相同的get / set代码一遍又一遍。
我需要所有属性的Intellisense,这排除了使用例如。 ExpandoObject
。
如果我在C / C ++领域,我可以使用宏,例如:
#define DEFPROP(name) \
public string name \
{ \
get { return _getValue(); } \
set { _setValue(value); } \
} \
然后:
public class Abc
{
DEFPROP(City)
DEFPROP(State)
...
}
但当然这是C#。
那么......任何聪明的想法?
####编辑###
我想我原来的帖子不够清楚
我的帮助函数_getValue()做了一些自定义的查找&基于正在调用Property的处理。它不仅仅存储/检索一个简单的道具特定值
如果我需要的只是简单值,那么我只使用自动属性
public string { get; set; }
并完成它,并且不会问这个问题。
答案 0 :(得分:5)
首先关闭:CallerMemberNameAttribute
会注入主叫成员名称,因此无需反思:
public class Abc
{
public string City
{
get { return _getValue(); }
set { _setValue(value); }
}
public string State
{
get { return _getValue(); }
set { _setValue(value); }
}
private string _getValue([CallerMemberName] string memberName = "")
{
}
private void _setValue(string value,
[CallerMemberName] string memberName = "")
{
}
}
其次:通过利用T4模板生成.cs文件,可以实现类型成员的生成:
<#@ output extension=".cs" #>
<#
var properties = new[]
{
"City",
"State"
};
#>
using System.Runtime.CompilerServices;
namespace MyNamespace
{
public class Abc
{
<# foreach (var property in properties) { #>
public string <#= property #>
{
get { return _getValue(); }
set { _setValue(value); }
}
<# } #>
private string _getValue([CallerMemberName] string memberName = "") {}
private void _setValue(string value, [CallerMemberName] string memberName = "") {}
}
}
您甚至可以将_setValue
和_getValue
卸载到包含文件中,以便为其他模板提供可重用性。
T4模板确实具有优于宏的优势,可以随时重新生成代码。因此,即使在初始生成之后,也可以应用对源代码的调整(可能是实现自适应或属性重命名)。
答案 1 :(得分:1)
这是一个讨厌的hack,使用RealProxy和MarshalByRefObject,它可以让你拦截属性调用并做你想做的任何事情。
public class Abc : MarshalByRefObject
{
public string City { get; set; }
public string State { get; set; }
private Abc()
{
}
public static Abc NewInstance()
{
var proxy = new AbcProxy(new Abc());
return (Abc)proxy.GetTransparentProxy();
}
}
public class AbcProxy : RealProxy
{
private readonly Abc _realInstace;
public AbcProxy(Abc instance) : base(typeof (Abc))
{
_realInstace = instance;
}
public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
var methodInfo = methodCall.MethodBase as MethodInfo;
Console.WriteLine("Before " + methodInfo.Name);
try
{
var result = methodInfo.Invoke(_realInstace, methodCall.InArgs);
Console.WriteLine("After " + methodInfo.Name);
return new ReturnMessage(result, null, 0,
methodCall.LogicalCallContext, methodCall);
}
catch (Exception e)
{
return new ReturnMessage(e, methodCall);
}
}
}
然后当你像这样使用它时:
var x = Abc.NewInstance();
x.City = "hi";
var y = x.State;
您将在控制台窗口中看到以下内容:
Before set_City
After set_City
Before get_State
After get_State
答案 2 :(得分:0)
有人想出如何在c#
中使用c预处理器请参阅https://stackoverflow.com/a/15703757/417577
但是:您应该避免使用堆栈跟踪为您的目的!将字符串传递给_getValue(“name”)或自动生成字段。如果使用堆栈跟踪,则必须停用方法内联(简单)以及尾调用优化(不确定是否可行)。
答案 3 :(得分:0)
我制作了一个Visual Studio代码段,为您自动生成代码。
这是.snippet
文件内容:
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets
xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>MyProp</Title>
<Author>ryanyuyu</Author>
<Description>Auto get/set property</Description>
<Shortcut>myprop</Shortcut>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>propName</ID>
<Default>MyProperty</Default>
<ToolTip>The name of the property.</ToolTip>
</Literal>
</Declarations>
<Code Language="CSharp">
<![CDATA[
public string $propName$
{
get { return _getValue(); }
set { _setValue(value); }
}
]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
将此XML文档另存为.snippet
文件。然后,只需按照MSDN To Add a Code Snippet to Visual Studio
输入<ShortCut>
块中的任何内容(当前为myprop
)后,只需按 Tab 即可让Visual Studio文本编辑器为您插入。