[英]C#: how to define an extension method as “with” in F#?
F#有一個方便的功能“with”,例如:
type Product = { Name:string; Price:int };;
let p = { Name="Test"; Price=42; };;
let p2 = { p with Name="Test2" };;
F#創建關鍵字“with”作為記錄類型默認是不可變的。
現在,是否可以在C#中定義類似的擴展? 看起來有點棘手,就像在C#中我不確定如何轉換字符串
Name="Test2"
代表或表達?
public static T With<T, U>(this T obj, Expression<Func<T, U>> property, U value)
where T : ICloneable {
if (obj == null)
throw new ArgumentNullException("obj");
if (property == null)
throw new ArgumentNullException("property");
var memExpr = property.Body as MemberExpression;
if (memExpr == null || !(memExpr.Member is PropertyInfo))
throw new ArgumentException("Must refer to a property", "property");
var copy = (T)obj.Clone();
var propInfo = (PropertyInfo)memExpr.Member;
propInfo.SetValue(copy, value, null);
return copy;
}
public class Foo : ICloneable {
public int Id { get; set; }
public string Bar { get; set; }
object ICloneable.Clone() {
return new Foo { Id = this.Id, Bar = this.Bar };
}
}
public static void Test() {
var foo = new Foo { Id = 1, Bar = "blah" };
var newFoo = foo.With(x => x.Bar, "boo-ya");
Console.WriteLine(newFoo.Bar); //boo-ya
}
或者,使用復制構造函數:
public class Foo {
public Foo(Foo other) {
this.Id = other.Id;
this.Bar = other.Bar;
}
public Foo() { }
public int Id { get; set; }
public string Bar { get; set; }
}
public static void Test() {
var foo = new Foo { Id = 1, Bar = "blah" };
var newFoo = new Foo(foo) { Bar = "boo-ya" };
Console.WriteLine(newFoo.Bar);
}
喬治的出色建議略有不同,允許多項任務:
public static T With<T>(this T obj, params Action<T>[] assignments)
where T : ICloneable {
if (obj == null)
throw new ArgumentNullException("obj");
if (assignments == null)
throw new ArgumentNullException("assignments");
var copy = (T)obj.Clone();
foreach (var a in assignments) {
a(copy);
}
return copy;
}
public static void Test() {
var foo = new Foo { Id = 1, Bar = "blah" };
var newFoo = foo.With(x => x.Id = 2, x => x.Bar = "boo-ya");
Console.WriteLine(newFoo.Bar);
}
我可能會使用第二個,因為(1)任何通用解決方案將會不必要地緩慢和復雜化; (2)它具有與您想要的語法最接近的語法(並且語法符合您的期望); (3)F#復制和更新表達式的實現類似。
也許是這樣的:
void Main()
{
var NewProduct = ExistingProduct.With(P => P.Name = "Test2");
}
// Define other methods and classes here
public static class Extensions
{
public T With<T>(this T Instance, Action<T> Act) where T : ICloneable
{
var Result = Instance.Clone();
Act(Result);
return Result;
}
}
作為lambda函數的替代方法,您可以使用具有默認值的參數。 唯一的小問題是你必須選擇一些默認值,這意味着不要更改此參數 (對於引用類型),但null
應該是一個安全的選擇:
class Product {
public string Name { get; private set; }
public int Price { get; private set; }
public Product(string name, int price) {
Name = name; Price = price;
}
// Creates a new product using the current values and changing
// the values of the specified arguments to a new value
public Product With(string name = null, int? price = null) {
return new Product(name ?? Name, price ?? Price);
}
}
// Then you can write:
var prod2 = prod1.With(name = "New product");
你必須自己定義方法,但情況總是如此(除非你要使用效率較低的反射)。 我認為語法也相當不錯。 如果你想讓它像F#一樣好,那么你將不得不使用F#:-)
在C#中沒有原生能力來做這個擴展方法,但成本是多少? a和b是一個原因,立即混亂引用類型和任何建議,b為基礎(“有”),作為對我們有多少對象一起工作。 只有一個嗎? 是B 的副本? b指向一個 ?
C#不是F#。
請看Eric Lippert回答的我之前的SO問題:
“編寫清晰代碼的經驗法則之一是: 將所有副作用放在語句中;非語句表達式應該沒有副作用。 ”
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.