簡體   English   中英

C#:如何在F#中將擴展方法定義為“with”?

[英]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#中沒有原生能力來做這個擴展方法,但成本是多少? ab一個原因,立即混亂引用類型和任何建議,b為基礎(“有”),作為對我們有多少對象一起工作。 只有一個嗎? B 副本? b指向一個

C#不是F#。

請看Eric Lippert回答的我之前的SO問題:

“編寫清晰代碼的經驗法則之一是: 將所有副作用放在語句中;非語句表達式應該沒有副作用。

更流利的C#/ .NET

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM