7

I'd like to mimic the F# 'with' keyword (which can be used on records) in C#.

For now, when i create a new immutable class, I just add manually some custom "with" methods like this :

public class MyClass
{
    public readonly string Name;
    public readonly string Description;

    public MyClass(string name, string description)
    {
        this.Name = name;
        this.Description = description;
    }

    // Custom with methods
    public MyClass WithName(string name)
    {
        return new MyClass(name, this.Description);
    }

    public MyClass WithDescription(string description)
    {
        return new MyClass(this.Name, description);
    }
}

For my personal c# development, I tried to create a generic method to do this (in a perfect world, I would use F#). The "best" I did is something like :

    public static class MyExtensions
    {
        public static TSource With<TSource, TField>(
            this TSource obj, 
            string fieldName, 
            TField value) 
            where TSource : class
        {
            // Reflection stuff to use constructor with the new value 
            // (check parameters names and types)...
        }
    }

It works but I'm not quite satisfied because I lose compile time errors by using a string parameter (for now I don't care of performance issues).

I'd really like to be able to write something like the code below, where I replace the string parameter by a "projection" lambda :

var myClass1 = new MyClass("name", "desc");
var myClass2 = myClass1.With(obj => obj.Name, "newName");

My extension method would look something like :

    public static TSource With<TSource, TField>(
        this TSource obj, 
        Expression<Func<TSource, TField>> projection, 
        TField value)
        where TSource : class
    {
        // TODO
    }

So here are my questions :

  • Is it possible to use reflection on the projection's result and get the field name from it ?
  • Has someone else already done a robust "with" method in C# ?
John Saunders
  • 160,644
  • 26
  • 247
  • 397
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Nov 17 '14 at 21:46
  • For your first question, see http://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression. Your second isn't really on-topic for StackOverflow I'm afraid. – dav_i Nov 17 '14 at 21:47
  • 1
    Also see http://stackoverflow.com/questions/2820660/get-name-of-property-as-a-string – Igby Largeman Nov 17 '14 at 21:51
  • Your `Expression` still isn't mandating type safety. Someone can still call it by doing something like `With(obj => "foo", "bar")` and you have no real way to deal with that. – Servy Nov 17 '14 at 21:54
  • Your best bet is to create builder objects for these immutable objects. Have mutable versions of the same type, and conversion methods/operators to move back and forth between the two. You can then take an object, convert it to the builder object, set some number of values, and then convert it back. – Servy Nov 17 '14 at 21:56
  • Also remember that you can use both C# and F# together in the same solution so you can have the best of both worlds! – dav_i Nov 17 '14 at 22:02

2 Answers2

2

This can certainly be done and is approach that is used in several places including Entity Framework. In EF it is used for including properties in a query.

DbContext.Categories.Include(c=>c.Products)

A query over this set will also pull the products in a category. You can examine the expression in your extension method and extract the member info like this

((System.Linq.Expressions.MemberExpression)projection.Body).Member

Of course in practice you will need some error handling to make sure the expression is a member expression. Then you can use reflection to set the appropriate property. You will need a setter for this to work but you can access private setters via reflection so the types can still be effectively immutable. This is not a perfect solution but I guess it is close enough.

I am not aware of any existing implementation of such a method. I am curious to see your full version.

Stilgar
  • 22,354
  • 14
  • 64
  • 101
0

Because F# and C# are both .NET languages and thus compile to the same IL, you could also consider how F# does its work under the hood. There are a couple ways to see this-

  • Compile some simple F# code, and then decompile it into C# using an IL to cdoe tool such as DotPeek.
  • Compile some F# code, and examine it with an IL-inspection tool such as ILDasm

though the second option requires some working knowledge of IL, it could also prove more useful in seeing some of the subtle differences that C# may not be able to compile to at all.

David
  • 10,458
  • 1
  • 28
  • 40