我们有一些大型类,其中包含具有基本相同的简单访问器的属性讲解员:
public ObservableField<T>
{
internal T Val;
public ObservableFile(T val) => this.Val ) val;
...
}
public class DataConfiguration
{
private ObservableField<bool> isEnabled;
public bool IsEnabled
{
get => GetValue( ref isEnabled, nameof(IsEnabled));
set
{
if(SetValue( ref isEnabled, nameof(IsEnabled), value))
OnDataChanged(nameof(IsEnabled));
}
}
private ObservableField<WrapMode> dataWrapMode;
public bool DataWrapMode
{
get => GetValue( ref dataWrapMode, nameof(DataWrapMode));
set
{
if(SetValue( ref dataWrapMode, nameof(DataWrapMode), value))
OnDataChanged(nameof(DataWrapMode));
}
}
...
}
我们想要更改此设置的原因是为了使其更加简洁且不易出错,因为代码库早于 C#4。所以没有
nameof
并且出现了复制粘贴错误:
private ObservableField<bool> dirty;
public bool Dirty
{
get => GetValue( ref dirty, "Dirty");
set
{
if(SetValue( ref dirty, "Dirty", value))
OnDataChanged(**"IsEnabled"**);
}
}
我们正在寻找最好具有 T4 代码生成的解决方案。目前无法更改
ObservableField
,因为它在较大的代码库中普遍存在,并且该类型的某些属性需要不同的处理。
我也无法对其使用包装器,因为属性和反射有很多魔力,可以发现 GUI 中显示的属性。
使用 C 风格的宏会很简单(但不漂亮或不惯用):
#define OBSERVABLE_WITH_ACCESSORS(name,type) \
private ObservableField<type> _##name; \
public ##type ##name \
{ \
get => GetValue( ref _##name, nameof(##name)); \
set \
{ \
if(SetValue( ref _##name, nameof(##name), value)) \
OnDataChanged(nameof(##name)); \
} \
}
这如何在最好的 c#-8.0 中完成? 如果在 C#-8.0 中无法轻松完成,建议使用更新的标准也可以,因为我们确实打算在将来进行迁移。
您可以采取一些措施来减少样板/复制粘贴错误,而无需使用 T4/源生成器:
CallerMemberNameAttribute
而不是硬编码名称OnDataChanged
通话我不知道你的
GetData
/ SetData
目前是如何定义的,但请考虑以下内容:
private T GetData<T>(ref ObservableField<WrapMode> field, [CallerMemberName] string propertyName? = null)
{
// What your `GetData` currently does, using `propertyName`
}
private void SetData<T>(ref ObservableField<WrapMode> field, T value, [CallerMemberName] string propertyName? = null
{
if (/* What your `SetData` currently does */)
OnDataChanged(propertyName);
}
用法如下:
private ObservableField<bool> isEnabled;
public bool IsEnabled
{
get => GetValue(ref isEnabled);
set => SetValue(ref isEnabled, value);
}
对于 T4 生成,您需要在程序集的编译版本上使用反射,以便生成要添加到该程序集的代码,这有点像先有鸡还是先有蛋的情况。
C# 的源生成器将是一个完美的选择,因为它们能够生成代码以在编译时注入到程序集中,并且它们可以访问程序集中的类型。
当它们第一次出现时,
INotifyPropertyChanged
有很多相当简单的示例,给定一个仅包含支持字段定义的类生成属性(这实际上就是您正在做的事情)。但是,请注意,旧的 ISourceGenerator
接口已被弃用,取而代之的是 IIncrementalGenerator
,并且大多数旧示例都使用 ISourceGenerator
。