Object Initializer with expression - c#

Is it possible to inilialize an object like this?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApp3
{
class Student
{
public bool IsStudent { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
/*
************Concise but not complied***********
var student = GetDefault()
{
Age = 18,
Name = "Nick"
};
*/
var student = GetDefault();
student.Age = 18;
student.Name = "Nick";
Student GetDefault()
{
var stu = new Student()
{
IsStudent = true
};
return stu;
}
}
}
}
I think the repeated "student." is redundant.
What I want to talking about is a possiable C# syntactic sugar,not object initialize solution.The properties of Student may be a lot.
If it's not possible,it's apreciate to tell the possible design reason.

I agree with HimBromBeere's statements. If you really want syntax close to what you described, you could do something like the following. That being said, obviously this now changes the meaning of the method and arguably this isn't really a great 'style' in my opinion... but it does achieve a syntax close to what you're asking for:
public class Student
{
public bool IsStudent { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
public static void Main(string[] args)
{
Student GetDefault(params Action<Student>[] modifiers)
{
var stu = new Student
{
IsStudent = true
};
if (modifiers != null)
{
foreach (var modifier in modifiers)
{
modifier(stu);
}
}
return stu;
}
var student = GetDefault(
s => s.Age = 18,
s => s.Name = "Nick"
);
}

You Should try something like this:
using System;
namespace Problem
{
public class Student
{
public bool IsStudent { get; set; }
public int? Age { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
// GetDefault.
Student Student_One = GetDefault();
Console.WriteLine("IsStudent = {0} \nName = {1} \nAge = {2}", Student_One.IsStudent, Student_One.Age, Student_One.Name);
Console.WriteLine("-------------------------");
Student Student_Two = GetDefault();
Student_Two.Age = 19;
Student_Two.Name = "Nick";
Console.WriteLine("IsStudent = {0} \nName = {1} \nAge = {2}", Student_Two.IsStudent, Student_Two.Age, Student_Two.Name);
Console.WriteLine("-------------------------");
Student Student_Three = new Student
{
IsStudent = true,
Age = 20,
Name = "Johnson"
};
Console.WriteLine("Name = {0} & Age = {1}" , Student_Three.Age, Student_Three.Name);
Console.WriteLine();
}
static Student GetDefault()
{
Student student = new Student
{
IsStudent = true
};
return student;
}
}
}
Output:

Your assumption - though comprehensible - is wrong. student. is not redundant.
Imagine someone would just call GetDefault without asigning its result to a variable - which is pure fine and valid.
GetDefault()
{
...
}
GetDefault returns an instance whose properties you modify afterwards. However as you don´t assign this object to any variable it will be garbage-collected soon. So when you set the properties it won´t have any efect at all, because there´s no code that may read it. In particular you can´t call DoSomethingWithUpdatedValues on that instance, because the instance is lost.
The syntax you want to avoid is the only valid syntax to achieve this. You could of course add parameters to your method, which however would change the methods purpose.
The cleanest solution in my opinion is to have a constructor that accepts the two parameters. There´s actually not much you can do on a Student that has no name or age at all, so just provide it to the constructor.

Related

How to create index based on nested array element in C# Mongodb strongly typed driver

This question is quite on the same principles as this one but I'd like to create an index using strongly typed approach on an object property when this object is nested in an array of the collection.
I can use:
new CreateIndexModel<T>( Builders<T>.IndexKeys.Ascending( a ) )
where a is an Expression which accesses to a direct property.
But I've found nothing similar to:
Builders<Library>.Filter.ElemMatch(x => x.Author.Books, b => b.IsVerified == false));
so that I can define as index some field of an object nested in an array which is a member of the collection.
Is that possible to do it and how?
Consider the following data model:
public class Course
{
public string Name { get; set; }
public string Teacher { get; set; }
}
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public ReadOnlyCollection<Course> Courses { get; set; }
}
You can create an ascending multikey index on the field Courses in the following manner:
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public static class Program
{
private static MongoClient Client;
private static IMongoDatabase Database;
private static IMongoCollection<Student> Collection;
public static async Task Main(string[] args)
{
Client = new MongoClient();
Database = Client.GetDatabase("test-index");
Collection = Database.GetCollection<Student>("students");
var courses1 = new List<Course>()
{
new Course { Name = "Math", Teacher = "Bob" }
}.AsReadOnly();
var courses2 = new List<Course>()
{
new Course { Name = "Computer Science", Teacher = "Alice" }
}.AsReadOnly();
var mark = new Student
{
Name = "Mark",
Courses = courses1,
Age = 20
};
var lucas = new Student
{
Name = "Lucas",
Courses = courses2,
Age = 22
};
await Collection.InsertManyAsync(new[] { mark, lucas }).ConfigureAwait(false);
var model = new CreateIndexModel<Student>(
Builders<Student>.IndexKeys.Ascending(s => s.Courses));
await Collection.Indexes.CreateOneAsync(model).ConfigureAwait(false);
Console.WriteLine("All done !");
}
}
}
This query is served by the index you created:
db.students.find({Courses: {"Name": "Math", "Teacher": "Bob"}})
If you don't want to create an index on the entire array Courses, but instead you want an index on the field Name of the nested object (the Course object), this is the way to go:
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ConsoleApp1
{
public static class Program
{
private static MongoClient Client;
private static IMongoDatabase Database;
private static IMongoCollection<Student> Collection;
public static async Task Main(string[] args)
{
Client = new MongoClient();
Database = Client.GetDatabase("test-index");
Collection = Database.GetCollection<Student>("students");
var courses1 = new List<Course>()
{
new Course { Name = "Math", Teacher = "Bob" }
}.AsReadOnly();
var courses2 = new List<Course>()
{
new Course { Name = "Computer Science", Teacher = "Alice" }
}.AsReadOnly();
var mark = new Student
{
Name = "Mark",
Courses = courses1,
Age = 20
};
var lucas = new Student
{
Name = "Lucas",
Courses = courses2,
Age = 22
};
await Collection.InsertManyAsync(new[] { mark, lucas }).ConfigureAwait(false);
var model = new CreateIndexModel<Student>(
Builders<Student>.IndexKeys.Ascending("Courses.Name"));
await Collection.Indexes.CreateOneAsync(model).ConfigureAwait(false);
Console.WriteLine("All done !");
}
}
}
This query is served by the index you created: db.students.explain("executionStats").find({"Courses.Name": "Math"})
A possible way to avoid the usage of magic strings in my second example is exploiting the power of the nameof C# operator:
$"{nameof(Student.Courses)}.{nameof(Course.Name)}"
here's the strongly typed way to created indexes for nested fields using the MongoDB.Entities convenience library. [disclaimer: i'm the author]
using MongoDB.Entities;
using System.Collections.Generic;
namespace StackOverflow
{
public class Program
{
public class Parent : Entity
{
public Child[] Children { get; set; }
}
public class Child
{
public List<Friend> Friends { get; set; }
}
public class Friend
{
public string Name { get; set; }
}
static void Main(string[] args)
{
new DB("test");
DB.Index<Parent>()
.Key(p => p.Children[-1].Friends[-1].Name, KeyType.Ascending)
.Create();
}
}
}
the above creates an ascending index on the name field which is nested two levels deep with the following command:
db.Parent.createIndex({
"Children.Friends.Name": NumberInt("1")
}, {
name: "Children.Friends.Name(Asc)",
background: true
})

Using a Parameter to Call a Class in C#

I have got a method with a single parameter of string class that I need to use to access another class in my project.
The following will be what I want it to do. Note that this syntax gives errors.
public string getId(string name) {
string Id = name.GetId();
return Id;
}
Assuming that the user enters "Joe" as the name, one would go to the class Joe.cs, which looks like this.
public class Joe {
public string Id = 32;
public string GetId() {
return Id;
}
}
What I want to happen here is for the first method to be able to get the GetId method from Joe if Joe is entered as a parameter. How would I do this? Thank you all.
This might point you in a better direction
The idea is to have a class called User that holds user information (funnily enough)
This way you can have a list of users (not a class for each one), as such you can easily look up a user and mess with them as much as you want
public class User
{
public string UserName { get; set; }
public string FavoriteColor { get; set; }
}
public class Program
{
// A List to hold users
private static List<User> _users = new List<User>();
private static void Main(string[] args)
{
// lets add some people
_users.Add(new User() { UserName = "Bob",FavoriteColor = "Red" });
_users.Add(new User() { UserName = "Joe", FavoriteColor = "Green" });
_users.Add(new User() { UserName = "Fred", FavoriteColor = "Blue" });
// use a linq query to find someone
var user = _users.FirstOrDefault(x => x.UserName == "Bob");
// do they exist?
if (user != null)
{
// omg yay, gimme teh color!
Console.WriteLine(user.FavoriteColor);
}
}
}
Output
Red
You can take it a step further and ask the user to look up other users (what a time to be a alive!)
Console.WriteLine("Enter a user (case sensitive)");
var userName = Console.ReadLine();
var user = _users.FirstOrDefault(x => x.UserName == userName);
if (user != null)
{
Console.WriteLine(user.FavoriteColor);
}
else
{
Console.WriteLine("Game over, you failed");
}
Console.ReadLine();
You could build a Class that can contain the details you want to store, then build a Manager class that exposes public methods that can extract informations from the stored objects:
public class Friend : IComparable<Friend>
{
public Friend() { }
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int FriendshipLevel { get; set; }
int IComparable<Friend>.CompareTo(Friend other)
{
if (other.FriendshipLevel > this.FriendshipLevel) return -1;
else return (other.FriendshipLevel == this.FriendshipLevel) ? 0 : 1;
}
}
public class MyFriends : List<Friend>
{
public MyFriends() { }
public int? GetID(string FriendName)
{
return this.Where(f => f.FirstName == FriendName).FirstOrDefault()?.ID;
}
public Friend GetFriendByID(int FriendID)
{
return this.Where(f => f.ID == FriendID).FirstOrDefault();
}
}
Build a sample class:
MyFriends myFriends = new MyFriends()
{
new Friend() { ID = 32, FirstName = "Joe", LastName = "Doe", FriendshipLevel = 100},
new Friend() { ID = 21, FirstName = "Jim", LastName = "Bull", FriendshipLevel = 10},
new Friend() { ID = 10, FirstName = "Jack", LastName = "Smith", FriendshipLevel = 50},
};
Then you can extract informations on single/multiple objects using the public methods of the "Manager" class:
int? ID = myFriends.GetID("Joe");
if (ID.HasValue) // Friend Found
Console.WriteLine(ID);
//Search a friend by ID
Friend aFriend = myFriends.GetFriendByID(32);
if (aFriend != null)
Console.WriteLine($"{aFriend.FirstName} {aFriend.LastName}");
Or you can use LINQ to get/aggregate the required informations directly if there isn't a public method that fits:
// Get your best friends using LINQ directly
List<Friend> goodFriends = myFriends.Where(f => f.FriendshipLevel > 49).ToList();
goodFriends.ForEach((f) => Console.WriteLine($"{f.FirstName} {f.LastName}"));
//Best friend
Friend bestFriend = myFriends.Max();
With respect to your Question : GetId method from Joe if Joe is entered as a
parameter.
You can achieve that in the following ways also:
2. Method Using Named Method.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Problem
{
// Simple Model Class.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
// Delegate.
public delegate string PersonHandler(Person person);
static void Main(string[] args)
{
List<Person> _persons = new List<Person>()
{
new Person(){Id=1,Name="Joe"},
new Person(){Id=2,Name="James"},
new Person(){Id=3,Name="Nick"},
new Person(){Id=4,Name="Mike"},
new Person(){Id=5,Name="John"},
};
PersonHandler _personHandler = new PersonHandler(GetIdOfPerson);
IEnumerable<string> _personIds = _persons.Select(p => _personHandler.Invoke(p));
foreach (var id in _personIds)
{
Console.WriteLine(string.Format("Id's : {0}", id));
}
}
// This is the GetId Method.
static string GetIdOfPerson(Person person)
{
string Id = person.Id.ToString();
return Id;
}
}
}
2. Method Using Anonymous Moethod.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Problem
{
// Simple Model Class.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
// Delegate.
public delegate string PersonHandler(Person person);
static void Main(string[] args)
{
List<Person> _persons = new List<Person>()
{
new Person(){Id=1,Name="Joe"},
new Person(){Id=2,Name="James"},
new Person(){Id=3,Name="Nick"},
new Person(){Id=4,Name="Mike"},
new Person(){Id=5,Name="John"},
};
PersonHandler _personHandler = delegate(Person person)
{
string id = person.Id.ToString();
return id;
};
// Retrieving all person Id's.
IEnumerable<string> _personIds = _persons.Select(p => _personHandler.Invoke(p));
foreach (var id in _personIds)
{
Console.WriteLine(string.Format("Id's : {0}", id));
}
}
}
}
2. Method Using a Lambda Expression.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Problem
{
// Simple Model Class.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
// Delegate.
public delegate string PersonHandler(Person person);
static void Main(string[] args)
{
List<Person> _persons = new List<Person>()
{
new Person(){Id=1,Name="Joe"},
new Person(){Id=2,Name="James"},
new Person(){Id=3,Name="Nick"},
new Person(){Id=4,Name="Mike"},
new Person(){Id=5,Name="John"},
};
PersonHandler _personHandler = (Person person) => person.Id.ToString();
IEnumerable<string> _personIds = _persons.Select(p => _personHandler.Invoke(p));
foreach (var id in _personIds)
{
Console.WriteLine(string.Format("Id's : {0}", id));
}
}
}
}
Output:

How do i properly initialize a variable with a custom type

This is the code in C#:
public bool IsNation(string country)
{
for (int i = 0; i < Nations.Count; i++)
{
if (Nations[i].Name == country)
{
return true;
}
else { return false; }
}return true;
}
In C# you must initialize variables. But what if you made one of your own like this below?
public class WorldMarket
{
public WorldMarket()
{
Nations = new List<NationBuilder>();
}
internal List<NationBuilder> Nations { get; set; }
public void AddToWorldMarket(NationBuilder nation)
{
Nations.Add(nation);
}
The main idea is that from this structure:
- wm
- Nations
- [0]
- Name "USA"
- stockpile
- [0]
- Name "Coal"
- Quantity "quantity"
- Value "value"
- [1] //Same as above
Find the country Name "USA" or whatever name inside this structure with a function that by only inserting a string with a name it outputs {1 or 0}
or True or False (if type == bool).
My attempt is the first code presented in this question. It tries to "travel" the structure and find the Name Tag you input as country using this call.
IsNation(string country); where country can be whatever string input.
Question
If C# wants me to declare every variable with an initial value, how do i do it with this custom or whatever custom type i may do?
Initialize the variable in the constructor public WorkdMarket(). See code below :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
WorldMarket wm = new WorldMarket();
}
}
public class WorldMarket
{
internal List<NationBuilder> Nations { get; set; }
public WorldMarket()
{
Nations = new List<NationBuilder>() {
new NationBuilder() {
name = "USA",
stockPiles = new List<StockPile>() {
new StockPile() { name = "Coal", quantity = 2, value = "value"},
new StockPile() { name = "Coal", quantity = 2, value = "value"}
}
}
};
}
public void AddToWorldMarket(NationBuilder nation)
{
Nations.Add(nation);
}
}
public class NationBuilder
{
public string name { get; set; }
public List<StockPile> stockPiles { get; set; }
}
public class StockPile
{
public string name { get; set; }
public int quantity { get; set; }
public string value { get; set; }
}
}
The line you requested would be:
WorldMarket con = new WorldMarket();
However, this would initialize as a new WorldMarket object, which has no pre-populated values yet. If the nations are meant to be static, you could initialize all the nations within the WorldMarket class
public class WorldMarket
{
public WorldMarket()
{
Nations = new List<NationBuilder>() {
new NationBuilder() {
name = "USA",
...
},
new NationBuilder() {
name = "AUS",
...
}
}
}
}
Or alternatively if you could have your isNation method within WorldMarket, that might work better such that.
public class WorldMarket()
{
// various class constructor methods
public int IsNation(string country)
{
// you could access Nations directly here
for (int i = 0; i < Nations.Count; i++)
{
if (Nations[i].Name == country)
{
return 1;
}
// else { return 0; } -- this would exit the loop unnecessarily
}
return 0;
}
}
and the usage in your main program would be something like
program static void Main(string[] args)
{
WorldMarket con = new WorldMarket();
con.AddToWorldMarket(new NationBuilder() {
name = "USA",
...
}
Console.WriteLine(con.IsNation("USA"));
}
}

Adding values using LINQ to an object property with a Dictionary property

I have a dataset which returns a couple of contact information in string(Phone, mobile, skype). I created an object with a Dictionary property where i can put the contact information in a key value pair. The problem is, I am assigning the values of the object using Linq. Hope somebody can help. Here is my code:
public class Student
{
public Student()
{
MotherContacts = new ContactDetail();
FatherContacts = new ContactDetail();
}
public ContactDetail MotherContacts { get; set; }
public ContactDetail FatherContacts { get; set; }
}
public class ContactDetail
{
public ContactDetail()
{
Items = new Dictionary<ContactDetailType, string>();
}
public IDictionary<ContactDetailType, string> Items { get; set; }
public void Add(ContactDetailType type, string value)
{
if(!string.IsNullOrEmpty(value))
{
Items.Add(type, value);
}
}
}
public enum ContactDetailType
{
PHONE,
MOBILE
}
Here's how I assign value to the Student object:
var result = ds.Tables[0].AsEnumerable();
var insuranceCard = result.Select(row => new Student()
{
MotherContacts.Items.Add(ContactDetailType.PHONE, row.Field<string>("MotherPhone"),
MotherContacts.Items.Add(ContactDetailType.MOBILE, row.Field<string>("MotherMobile")
}).FirstOrDefault();
The compiler says that the MotherContacts is not recognized in the context. What should I do?
I think your code should look like:
var insuranceCard = result.Select(row =>
{
var s = new Student();
s.MotherContacts.Items.Add(ContactDetailType.PHONE, row.Field<string>("MotherPhone");
s.MotherContacts.Items.Add(ContactDetailType.MOBILE, row.Field<string>("MotherMobile");
return s;
}).FirstOrDefault();
You are using the object initializer syntax in a wrong way. The correct use is:
new Student{MotherContacts = value} where value must be a ContactDetail.

Get values from a HashSet<T> using reflection

I need a way to get the values from a generic HashSet using reflection. Here is what I've tried (you can copy/paste this on a console app):
class Program
{
public class Order
{
public int Id { get; set; }
}
public class Person
{
public string Name { get; set; }
public ICollection<Order> Orders { get; set; }
}
static void Main(string[] args)
{
var person = new Person();
person.Name = "Test Person";
person.Orders = new HashSet<Order>();
person.Orders.Add(new Order() { Id = 1 });
person.Orders.Add(new Order() { Id = 2 });
var reflectedOrders = person.GetType().GetProperty("Orders").GetValue(person, null);
Console.WriteLine("How do I iterate the reflected orders?");
Console.ReadLine();
}
}
EDIT
It's an example, in the real application I don't know which type to convert the reflected Orders. I only know the property is an ICollection<T> (turned to HashShet by EF)
Did you tried casting reflectedOrders to IEnumerable?
IEnumerable reflectedOrders = (IEnumerable)person.GetType().GetProperty("Orders").GetValue(person, null);
It should be simple as casting:
var reflectedOrders = (HashSet<Order>) person.GetType().GetProperty("Orders").GetValue(person, null);
foreach (var order in reflectedOrders)
...
What about
var orders = persons.OfType<Person>().SelectMany(p => p.Orders);

Categories