「突變賦值」概念源自動態語言,允許以單一運算式將多個屬性值批量指派給物件。C# 3.0 雖無原生語法支援,但結合匿名類型與擴充方法,可透過反射實現相近功能。
form.Text = "Hello World";
form.Top = 100;
form.Left = 200;
form := new { Text = "Hello World", Top = 100, Left = 200 };
using System.Reflection;
public static class MutantExtensions
{
///
/// 將來源匿名物件的屬性值複製到目標物件同名同型別屬性
///
public static void MAssign(this object target, object source)
{
if (target == null || source == null) return;
Type targetType = target.GetType();
Type sourceType = source.GetType();
foreach (PropertyInfo srcProp in sourceType.GetProperties())
{
if (!srcProp.CanRead) continue;
PropertyInfo targetProp = targetType.GetProperty(srcProp.Name, srcProp.PropertyType);
if (targetProp == null || !targetProp.CanWrite) continue;
object value = srcProp.GetValue(source, null);
targetProp.SetValue(target, value, null);
}
}
}
// WinForms 表單
Form form = new Form();
form.MAssign(new { Text = "Hello World", Top = 100, Left = 200 });
// 任意 POCO 物件
class Person { public string Name { get; set; } public int Age { get; set; } }
Person p = new Person();
p.MAssign(new { Name = "Tom", Age = 25 });
public static class MutantExtensions
{
public static void MAssign(this object target, object source, bool ignoreCase = false)
{
if (target == null || source == null) return;
var bindingFlags = BindingFlags.Public | BindingFlags.Instance;
var targetProps = target.GetType().GetProperties(bindingFlags)
.ToDictionary(p => ignoreCase ? p.Name.ToLower() : p.Name, p => p);
foreach (var srcProp in source.GetType().GetProperties(bindingFlags))
{
if (!srcProp.CanRead) continue;
string key = ignoreCase ? srcProp.Name.ToLower() : srcProp.Name;
if (!targetProps.TryGetValue(key, out var targetProp)) continue;
if (!targetProp.CanWrite) continue;
if (targetProp.PropertyType != srcProp.PropertyType) continue; // 嚴格型別匹配
targetProp.SetValue(target, srcProp.GetValue(srcProp, null));
}
}
// 僅複製指定屬性
public static void MAssign(this object target, object source, params string[] propertyNames)
{
var props = source.GetType().GetProperties()
.Where(p => propertyNames.Contains(p.Name, StringComparer.OrdinalIgnoreCase));
foreach (var srcProp in props)
{
var targetProp = target.GetType().GetProperty(srcProp.Name, srcProp.PropertyType);
if (targetProp?.CanWrite == true && srcProp.CanRead)
targetProp.SetValue(target, srcProp.GetValue(source));
}
}
}
PropertyInfo 或使用表達式樹編譯委派CanWrite),init 唯讀屬性(C# 9+)在建構後不可設定ref 擴充方法(C# 7.2+ ref struct 不支援擴充)var newPerson = person with { Name = "New" }; 建立新實例而非突變new Person { Name = "Tom", Age = 25 }此反射擴充方法適合舊版專案維護、動態物件更新、測試資料構建等場景,新專案建議採用不可變模式與專業映射庫。