9

When using Kotlin, one could use apply to set multiple properties of an existing object and keeping the code cleaner, for example instead of:

person.firstName = "John"
person.lastName = "Doe"
person.phone = "123 456 789"

We can use:

person.apply {
   firstName = "John"
   lastName = "Doe"
   phone = "123 456 789"
}

Is there an equivalent to the apply in C#?

The closest to this is the using but it can't be used this way as far as I know.

Edit: I know of object initializer in C#, but I'm actually looking for something that can be done for existing objects (for example an object fetched from the database).

Pavel Anikhouski
  • 21,776
  • 12
  • 51
  • 66
Ziad Akiki
  • 2,601
  • 2
  • 26
  • 41
  • Does this answer your question? [With block equivalent in C#?](https://stackoverflow.com/questions/481725/with-block-equivalent-in-c) – Chris Akridge Mar 14 '20 at 19:47
  • 1
    Yes, I'm aware of object initializer but actually the object might be for example fetched from the database and some modifications need to be made over multiple properties. Let me edit my question – Ziad Akiki Mar 14 '20 at 21:06
  • You can have a look at [records proposal](https://github.com/dotnet/csharplang/blob/master/proposals/records.md#) and [`with` expression](https://github.com/dotnet/csharplang/issues/77), but it's still in discussion and proposal state – Pavel Anikhouski Mar 14 '20 at 21:36
  • @PavelAnikhouski mmm looks a little bit different but we'll wait and see what it might come out in C#9 – Ziad Akiki Mar 14 '20 at 22:10
  • Note: Using isn’t related. – user2864740 Mar 15 '20 at 20:23

6 Answers6

9

Try this.... https://dev.to/amay077/kotlins-scope-functions-in-c-pbn

Code pasted below for convenience, but the above link is the source...

static class ObjectExtensions 
{
  // Kotlin: fun <T, R> T.let(block: (T) -> R): R
  public static R Let<T, R>(this T self, Func<T, R> block) 
  {
    return block(self);
  }

  // Kotlin: fun <T> T.also(block: (T) -> Unit): T
  public static T Also<T>(this T self, Action<T> block)
  {
    block(self);
    return self;
  }   
}

Can be used like this....

var model = new MyModel().Also(m => {
  m.Initialize();
  m.Load(path);
});
Karthick Nagarajan
  • 1,327
  • 2
  • 15
  • 27
user2430020
  • 91
  • 1
  • 1
4

There is currently no support in C# (version 8) for grouped multi-property assignment outside of object initialization.

Similar support exists in VB.NET and has been proposed for C# 9.

Little bit of historical context

In Visual Basic.NET there is similar statement - With:

    With person
        .FirstName = "John"
        .LastName = "Doe"
        .Phone = "123 456 789"
    End With

This one was carried from Visual Basic 6 for backward compatibility (previous, non .NET Version of language).

C# team (Anders Heilsberg himself told the story somewhere) argued that With statement decreases code readability and did not want to introduce it in the language. From what I have seen With statements can be nested and can creating quite a confusion of what is going on.

As many others have already mentioned, there is object initializer syntax that is quite similar:

var person = new Person
{
   firstName = "John",
   lastName = "Doe",
   phone = "123 456 789"
};

Future - C# 9

As pointed out in another (deleted) answer, there is an open proposal for records and With expression, to be added in C# 9:

person with { firstName = "John", lastName = "Doe", phone = "123 456 789" };

Bonus Tip

However, most important advice I can give you, to avoid annoying fellow C# developer who might work on your code - we don't use camelCase in C# for public properties and methods, because C# is not Java. We use PascalCase! :)

user2864740
  • 60,010
  • 15
  • 145
  • 220
Nenad
  • 24,809
  • 11
  • 75
  • 93
3

You can use it in this way with object initializers:

var person = new Person
{
   FirstName = "John",
   LastName = "Doe",
   Phone = "123 456 789"
};

Or with a constructor:

var person = new Person("John", "Doe", "123 456 789");

Your class would have to look like this for the constructor option:

class Person
{
    public Person(string firstName, string lastName, string phone)
    {
        FirstName = firstName;
        LastName = lastName;
        Phone = phone;
    }

    public string FirstName { get;set; }
    public string LastName { get;set; }
    public string Phone { get;set; }
}
Guapo
  • 3,446
  • 9
  • 36
  • 63
  • Yes, I'm aware of object initializer but actually the object might be for example fetched from the database and some modifications need to be made over multiple properties (I edited the question). – Ziad Akiki Mar 14 '20 at 21:09
  • 1
    Guapo has shown the best options available. There is no exact (current) equivalent to what you want in C# @ZiadAkiki. – mjwills Mar 14 '20 at 22:15
2

With an object initializer:

var person = new Person
{
   firstName = "John",
   lastName = "Doe",
   phone = "123 456 789"
};

After you already have the object, you can give yourself a short variable:

var p = person;
p.firstName = "Jane";
p.lastName = "Smith";
p.phone = "987 654 321";

//later:
Console.WriteLine(person.lastName); //will output "Smith"

which is less still less code than the apply option.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
1

Object initializers allow you to do that but only at instanciation of an object.

Like

var person = new Person
{
   FirstName = "John",
   LastName = "Doe",
   Phone = "123 456 789"
}
colinD
  • 1,641
  • 1
  • 20
  • 22
1

After copying Apply into several projects, i made a nuget package.

dotnet add package Object.Extensions.ScopeFunction

It offers extension methods Apply, ApplyForEach and Map (which lets you override return).

var permissionsMissingTestContext = new TestContext
{
    Users = GetStandardUsers().ApplyForEach(x => x.Permissions = new Permission[0]),
    SecurityPolicy = GetStandardSecurityPolicy().Apply(x => x.ShowWarningWhenPermissionsMissing = true),
    AnonymousPageUrl = GetStandardConfig().Map(x => new Url(x.PermissionsMissingScreenUrl)),
    Timeout = GetTestTimeout()?.Map(x => TimeSpan.FromSeconds(x)) ?? TimeSpan.FromSeconds(30)
}

Nuget

Ivan Koshelev
  • 3,830
  • 2
  • 30
  • 50