Инструмент, который вы можете рассмотреть, это T4 (T4 поставляется с VS2010, поэтому никаких дополнительных зависимостей не требуется).
T4 - это инструмент для генерации кода, который может помочь уменьшить поддержку «утомительного» кода. Подумайте ASP / PHP для кода. Это также похоже на XML / XSLT.
Отличное введение в T4 можно найти в блоге Олег Сыч .
Преимущества генерации кода в подобном случае заключаются в том, что, хотя сгенерированный код является избыточным, поддерживаемый вами код (шаблон T4) не является или, по крайней мере, менее избыточен.
Итак, подумав о предоставленном вами образце, я написал этот шаблон T4:
(Если вы хотите попробовать этот шаблон в Visual Studio, нажмите «Добавить новый элемент», выберите шаблон класса, но измените расширение с .cs на .tt, вставьте следующий источник в файл .tt и сохраните. После сохранения результат должен быть в соответствующий файл .cs)
// ReSharper disable InconsistentNaming
// ReSharper disable PartialMethodWithSinglePart
// ReSharper disable PartialTypeWithSinglePart
<#
// This is the "model" that is "what" we would like to generate
var classDefs = new []
{
new ClassDefinition
{
Name = "A",
Properties = new []
{
P ("int" , "Id" ),
P ("string" , "Name" ),
P ("B" , "FirstB" , listenToChanges:true ),
P ("B" , "SecondB" , listenToChanges:true ),
P ("B" , "ThirdB" , listenToChanges:true ),
},
},
new ClassDefinition
{
Name = "B",
Properties = new []
{
P ("int" , "Id" ),
P ("string" , "Name" ),
},
},
};
#>
namespace SO
{
using System;
using System.ComponentModel;
<#
// This part is the template ie "how" the model will be transformed into code
foreach (var classDef in classDefs)
{
#>
// ------------------------------------------------------------------------
/// <summary>
/// class <#=classDef.Name#> (implements INotifyPropertyChanged)
/// </summary>
public partial class <#=classDef.Name#> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged (string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler (this, new PropertyChangedEventArgs (name));
}
}
<#
foreach (var propertyDef in classDef.Properties)
{
#>
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property <#=propertyDef.Name#> (<#=propertyDef.Type#>)
/// </summary>
public <#=propertyDef.Type#> <#=propertyDef.Name#>
{
get { return <#=propertyDef.FieldName#>; }
set
{
if (<#=propertyDef.FieldName#> == value)
{
return;
}
<#
if (propertyDef.ListenToChanges)
{
#>
if (<#=propertyDef.FieldName#> != null)
{
<#=propertyDef.FieldName#>.PropertyChanged -= <#=propertyDef.ListenerName#>;
}
<#=propertyDef.FieldName#> = value;
if (<#=propertyDef.FieldName#> != null)
{
<#=propertyDef.FieldName#>.PropertyChanged += <#=propertyDef.ListenerName#>;
}
<#
}
else
{
#>
<#=propertyDef.FieldName#> = value;
<#
}
#>
<#=propertyDef.EventName#> ();
OnPropertyChanged("<#=propertyDef.Name#>");
}
}
// --------------------------------------------------------------------
<#=propertyDef.Type#> <#=propertyDef.FieldName#>;
// --------------------------------------------------------------------
partial void <#=propertyDef.EventName#> ();
// --------------------------------------------------------------------
<#
if (propertyDef.ListenToChanges)
{
#>
void <#=propertyDef.ListenerName#> (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (
"Instance of <#=classDef.Name#> detected a change of <#=propertyDef.Name#>.{0}",
e.PropertyName
);
<#=propertyDef.EventName#> ();
}
// --------------------------------------------------------------------
<#
}
}
#>
}
// ------------------------------------------------------------------------
<#
}
#>
}
<#+
class ClassDefinition
{
public string Name;
public PropertyDefinition[] Properties;
}
class PropertyDefinition
{
public string Type;
public string Name;
public bool ListenToChanges;
public string FieldName
{
get
{
return "_" + Name;
}
}
public string ListenerName
{
get
{
return Name + "_Listener";
}
}
public string EventName
{
get
{
return "Change_" + Name;
}
}
}
PropertyDefinition P (string type, string name, bool listenToChanges = false)
{
return new PropertyDefinition
{
Type = type ?? "<NO_TYPE>",
Name = name ?? "<NO_NAME>",
ListenToChanges = listenToChanges,
};
}
#>
Наконец это приводит к выводу:
// ReSharper disable InconsistentNaming
// ReSharper disable PartialMethodWithSinglePart
// ReSharper disable PartialTypeWithSinglePart
namespace SO
{
using System;
using System.ComponentModel;
// ------------------------------------------------------------------------
/// <summary>
/// class A (implements INotifyPropertyChanged)
/// </summary>
public partial class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged (string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler (this, new PropertyChangedEventArgs (name));
}
}
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property Id (int)
/// </summary>
public int Id
{
get { return _Id; }
set
{
if (_Id == value)
{
return;
}
_Id = value;
Change_Id ();
OnPropertyChanged("Id");
}
}
// --------------------------------------------------------------------
int _Id;
// --------------------------------------------------------------------
partial void Change_Id ();
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property Name (string)
/// </summary>
public string Name
{
get { return _Name; }
set
{
if (_Name == value)
{
return;
}
_Name = value;
Change_Name ();
OnPropertyChanged("Name");
}
}
// --------------------------------------------------------------------
string _Name;
// --------------------------------------------------------------------
partial void Change_Name ();
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property FirstB (B)
/// </summary>
public B FirstB
{
get { return _FirstB; }
set
{
if (_FirstB == value)
{
return;
}
if (_FirstB != null)
{
_FirstB.PropertyChanged -= FirstB_Listener;
}
_FirstB = value;
if (_FirstB != null)
{
_FirstB.PropertyChanged += FirstB_Listener;
}
Change_FirstB ();
OnPropertyChanged("FirstB");
}
}
// --------------------------------------------------------------------
B _FirstB;
// --------------------------------------------------------------------
partial void Change_FirstB ();
// --------------------------------------------------------------------
void FirstB_Listener (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (
"Instance of A detected a change of FirstB.{0}",
e.PropertyName
);
Change_FirstB ();
}
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property SecondB (B)
/// </summary>
public B SecondB
{
get { return _SecondB; }
set
{
if (_SecondB == value)
{
return;
}
if (_SecondB != null)
{
_SecondB.PropertyChanged -= SecondB_Listener;
}
_SecondB = value;
if (_SecondB != null)
{
_SecondB.PropertyChanged += SecondB_Listener;
}
Change_SecondB ();
OnPropertyChanged("SecondB");
}
}
// --------------------------------------------------------------------
B _SecondB;
// --------------------------------------------------------------------
partial void Change_SecondB ();
// --------------------------------------------------------------------
void SecondB_Listener (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (
"Instance of A detected a change of SecondB.{0}",
e.PropertyName
);
Change_SecondB ();
}
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property ThirdB (B)
/// </summary>
public B ThirdB
{
get { return _ThirdB; }
set
{
if (_ThirdB == value)
{
return;
}
if (_ThirdB != null)
{
_ThirdB.PropertyChanged -= ThirdB_Listener;
}
_ThirdB = value;
if (_ThirdB != null)
{
_ThirdB.PropertyChanged += ThirdB_Listener;
}
Change_ThirdB ();
OnPropertyChanged("ThirdB");
}
}
// --------------------------------------------------------------------
B _ThirdB;
// --------------------------------------------------------------------
partial void Change_ThirdB ();
// --------------------------------------------------------------------
void ThirdB_Listener (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (
"Instance of A detected a change of ThirdB.{0}",
e.PropertyName
);
Change_ThirdB ();
}
// --------------------------------------------------------------------
}
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
/// <summary>
/// class B (implements INotifyPropertyChanged)
/// </summary>
public partial class B : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged (string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler (this, new PropertyChangedEventArgs (name));
}
}
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property Id (int)
/// </summary>
public int Id
{
get { return _Id; }
set
{
if (_Id == value)
{
return;
}
_Id = value;
Change_Id ();
OnPropertyChanged("Id");
}
}
// --------------------------------------------------------------------
int _Id;
// --------------------------------------------------------------------
partial void Change_Id ();
// --------------------------------------------------------------------
// --------------------------------------------------------------------
/// <summary>
/// Gets or sets property Name (string)
/// </summary>
public string Name
{
get { return _Name; }
set
{
if (_Name == value)
{
return;
}
_Name = value;
Change_Name ();
OnPropertyChanged("Name");
}
}
// --------------------------------------------------------------------
string _Name;
// --------------------------------------------------------------------
partial void Change_Name ();
// --------------------------------------------------------------------
}
// ------------------------------------------------------------------------
}