Kotlin "apply" equivalent in C# - c#

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).

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);
});

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; }
}

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! :)

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.

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"
}

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

Related

Possibility to rearrange properties in object initializer

I would like to rearrange my properties inside a object initializer, by manually defining the new position. I found this Question but the first method only sorts the properties alphabetically, the other uses a different extension method.
new Student { StudentID = 2, Nationality = Nationalities.Austrian, StudentName = "Peter", Age = 21 }
A line like the above, I would like to rearrange using ReSharper or a Built-In Feature to this:
new Student { StudentID = 2, StudentName = "Peter", Age = 21, Nationality = Nationalities.Austrian }
Is there any possibility to achieve this?

How to delete whole line from text file, if any specific or more words exists in line

I created a text file and I am storing data to that file. Each word is separated by '-'
word1-word2-word3-word4.
Moreover, I am using DataGridView(control) to display each word in a separate column.
I want to remove specific line from text file where lets say 'word1' and 'word2' match to the given variables
public static string word1;
public static string word2;`
I am trying to implement below code but not getting proper solution to do that. please help. I am newbie to C#...
public static string word1; //static to access in another form
public static string word2;
if (e.ColumnIndex == 5) //view
{
string[] lines = File.ReadAllLines(#"info.txt");
word1 = dataGridViewF.Rows[e.RowIndex].Cells[0].Value.ToString();
word2= dataGridViewF.Rows[e.RowIndex].Cells[1].Value.ToString();
string[] newLines = lines.Where(line => !line.Contains(word1));
using (StreamWriter file = new StreamWriter(#"info.txt", true))
{
File.WriteAllLines(#"info.txt"", newLines);
}
}
Your example text line:
FirstName-LastName-age-MobileNo.
Is a big indicator that you are likely using the wrong approach. If a - delimits your data, what happens when a phone number has a - in it? What happens when a nice married person has a - in their new last name? Can you see how this would be problematic?
A better approach (as mentioned in the comments) would be to serialize your data into a format designed for this. I will use Json in my example.
First you should create a class that describes your data:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobilePhoneNumber { get; set; }
public int Age { get; set; } //Really should be storing birthday instead...
}
When you serialize this object into json it would look like this:
{
"FirstName": "John",
"LastName": "Doe",
"Age": 42,
"MobilePhoneNumber": "(123) 456-7890"
}
And a list of these will look like:
[{
"FirstName": "John",
"LastName": "Doe",
"Age": 42,
"MobilePhoneNumber": "(123) 456-7890"
},
{
"FirstName": "Jane",
"LastName": "Sanders-Doe",
"Age": 69,
"MobilePhoneNumber": "(890) 555-1234"
}]
So now your code for getting this data is pretty simple (Ill use Json.NET -- the standard library for this):
string json = File.ReadAllText(#"info.json");
List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(json);
Now that you have actual objects to work with, your code is much cleaner and less error prone:
string fName = dataGridViewF.Rows[e.RowIndex].Cells[0].Value.ToString();
string lName = dataGridViewF.Rows[e.RowIndex].Cells[1].Value.ToString();
string json = File.ReadAllText("info.json");
//'deserializes' which just means turns JSON into a real object
List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(json);
//'persons' is now a list of Persons who's names DONT match
//in other words the names that matched are removed from the output
persons = persons.Where(x => x.FirstName != fName && x.LastName != lName).ToList();
//'serialize' means turns an object into a json string
json = JsonConvert.SerializeObject(persons);
File.WriteAllText("info.json", json);
Note that I made the code a bit verbose so you could understand. A lot of the above lines can be condensed into less lines.
This is the best way to do it providing that you are getting fName and lName correctly:
string[] lines = File.ReadAllLines(#"info.txt"); //original file
fName = dataGridViewF.Rows[e.RowIndex].Cells[0].Value.ToString();
lName = dataGridViewF.Rows[e.RowIndex].Cells[1].Value.ToString();
var iter = lines.Where(line => {
string[] name = line.Split('-');
string f = name[0];
string l = name[1];
if(f.ToLower().Equals(fName.ToLower()))
if (l.ToLower().Equals(lName.ToLower()))
{
return false;
}
return true;
});
File.WriteAllLines(#"D:\\newfile.txt", iter.ToArray()); //put them in newfile.txt, don't delete the original one which is info.txt
Piece of advice: Don't use - as a delimiter. Because mobile numbers might contain dashes as well. I suggest you change the delimited to become a comma ,. However, FirstName and LastName being your first two params, won't affect your case. But in general, you want to use a delimiter that you know for sure it won't exist within your values.

How can I create lots of objects with different property values?

I'm programming in C# and I want to instantiate lots of new objects to my application, all of the same type, but with different values for their properties. Example:
Student student1 = new Student();
student1.Name = "James";
student1.Age = 19;
student1.City = "Los Angeles";
Student student2 = new Student();
student2.Name = "Karen";
student2.Age = 20;
student2.City = "San Diego";
Student student3 = new Student();
student3.Name = "Bob";
student3.Age = 20;
student3.City = "Dallas";
This way of coding seems really wrong to me because what if I didn't need 3, but 500 students? What would be the best way to do it then?
I tried to use a for loop for this but that doesn't work because the property values differ.
What is the most efficient way to do this?
Thanks in advance.
In order to do anything with your objects at runtime you will probably want them in a list.
Without reading from a file or database, etc., the most concise way might be :
var Students = new List<Student>{
new Student { Name = "Bob", Age = 22, City = "Denver" },
new Student { Name = "Sally", Age = 33, City = "Boston" },
new Student { Name = "Alice", Age = 12, City = "Columbus" }
};
I don't know your end goal however, is this just mock data, like for a test?
Add constructor to Student like this
Student (string name, int age, string city)
{
Name = name;
Age = age;
City = city;
}
///
Student student1 = new Student("James", 19, "Los Angeles");
Well, if what you mean by more efficient way to do it is just to write less code, you could instanciate them assigning the property's values at once, just like:
Student student1 = new Student() { Name = "James", Age = 19, City = "Los Angeles" };
If you want not just to write less code, but to - let's say - read the data from another source (like a Json list, or a TXT file) you will have to write a loader for it.
Well, it depends what you are going to use it for. If it’s for testing, then you could use a custom built tool to create random Students:
public class RandomStudentCreator
{
private readonly Random rnd = new Random();
private readonly IList<string> cities, names;
private readonly int minAge, maxAge;
public RandomStudentCreator(
IList<string> names,
IList<string> cities,
int minimumInckusiveAge,
int maximumExclusiveAge)
{
//Argument validation here
this.cities = cities;
this.names = names;
minAge = minimumInckusiveAge;
maxAge = maximumExclusiveAge;
}
public Student Next()
{
var student = new Student();
student.Name = names[rnd.Next(names.Count);
student.City = cities[rnd.Next(cities.Count);
Student.Age = rnd.Next(minAge, maxAge);
}
}
If this is production code, then you should be creating students based on:
User input
Some data backend (DB, text file, etc.)
But in any case, you don’t want to create a variable for each student. You probably want a collection of students. Depending on what you want to do with them, the type of collection you need may vary, the framework gives you plenty of options:
Arrays: Student[]
Lists: List<Student>
Queues: Queue<Student>
Stacks: Stack<Student>
Sets: HashSet<Student>
Etc.
And last but not least, you probably want to implement a constructor in Student that takes a name, city and age to make instantiation a little bit more compact than what you currently have:
public class Student
{
public Student(string name,
int age,
string city)
{
Name = name;
Age = age;
City = city;
}
//...
}
var john = new Student(“John”, 19, “LA”);
Programming is not about typing data. Need a lot of data? - Load them from files, databases, servers, through GUI, etc.
You can make a handy constructor, you can make factories and builders, but they are not for creating hundreds of objects in a row. Even if it is historical data, one day you will want to change them, fix something in them. Believe me, it's much easier to separate them from the code and store somewhere else, than to edit hundreds of lines of code later.
If you want 500 students I suggest extracting data to a file, database etc. student1..student499 implementation looks very ugly: let's organize them into array: Student[] students. As an example, let's use the simplest csv file Students.csv solution in the format
name,age,city
E.g.
name,age,city
James,19,Los Angeles
Karen,20,San Diego
Bob,20,Dallas
Having the file completed you can easily read it:
using System.IO;
using System.Linq;
...
Student[] students = File
.ReadLines("Students.csv")
.Where(line => !string.IsNullOrWhiteSpace(line)) // Skip empty lines
.Skip(1) // Skip header
.Select(line => line.Split(','))
.Select(items => new Student() {
Name = items[0],
Age = int.Parse(items[1]),
City = items[2], })
.ToArray();

Visual Studio Shortcu/Syntax for fast property assignment

Say Suppose you have a class
public class Person
{
public int PesronId{get;set;}
public string FirstName{get;set;}
public string LastName{get;set;}
public string Gender{get;set;}
}
Now We create an object p1
Person p1 = new Person();
Next we have values from textboxes to be assigned to p1
eg.
p1.PersonId = textbox1.text;
p1.FirstName = textbox2.text;
p1.LastName = textbox3.text;
Is there a more efficient way of doing this in Visual Studio 2010, by which I will get something like this
p1.PersonId =
p1.FirstName =
p1.LastName =
so that I dont have to manually type the properties for p1.
Or is then an alternate syntax that I can use.
There's simpler syntax for the code:
Person p1 = new Person
{
PersonId = textbox1.Text,
FirstName = textbox2.Text,
LastName = textbox3.Text
};
This is object initializer syntax, introduced in C# 3.
I think I'd misread the question though - it sounds like you're just interested in cutting down the typing required. There may be something which will do that, but personally I find IntelliSense is fine on its own. The readability of the code afterwards is much more important than the time spent typing, IMO.
You might also want to add a constructor to Person to take all the relevant property values - that would simplify things too, and with C# 4's named argument support, you can retain readability.
You can use the new initialization functionality in C#:
Person p1 = new Person()
{
PersonId = textbox1.text,
FirstName = textbox2.text,
LastName = textbox3.text
};

Initialize Object Properties in One Line of Code

Question:
Hello All,
Sorry that this is kind of a noob question. I just don't know how to word this process, so I'm not sure what to Google for. I'll put some C# code below that should explain what I'm trying to do. I just don't know how to do it in VB. Additionally, for future ref, if you could tell me what this process is called, it would be helpful to know. Thanks in advance for your help.
// Here is a simple class
public class FullName
{
public string First { get; set; }
public char MiddleInintial { get; set; }
public string Last { get; set; }
public FullName() { }
}
/* code snipped */
// in code below i set a variable equal to a new FullName
// and set the values in the same line of code
FullName fn = new FullName() { First = "John", MiddleInitial = 'J', Last = "Doe" };
Console.Write(fn.First); // prints "John" to console
As I mentioned earlier, I am drawing blanks on what to search for so sorry if this question is a repeat. I too hate reruns :) So, please link me somewhere else if you find something.
Solution:
So thanks to the help of one of our members, I have found that the keyword is With.
Dim fn As New FullName() With { .First = "John", .MiddleInitial = "J"c, .Last = "Doe" }
Console.Write(fn.First) ' prints "John" to console
This is an Object Initializer.
The equivelent VB.NET code would be:
Dim fn = New FullName() With {.First = "John", .MiddleInitial = 'J', .Last = "Doe" }
The VB.NET reference is on MSDN.
This feature is named Object Initializers. See here: http://www.danielmoth.com/Blog/2007/02/object-initializers-in-c-30-and-vb9.html
They are known as object initializers. You can find more information on them here.

Categories