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);
Related
I have a csv file that looks like this:
M;2017.12.01 17:04;1;example#example.com
T;1;K001;2
T;1;N001;1
M;2017.11.01 15:56;2;example#example.com
T;2;P001;2
T;2;P001;1
My problem is that I have to read this file into a List<> and be able to navigate in it with indexes but the different types of rows after the long ones are confusing me.
class Order
{
public string Type { get; set; }
public DateTime Date { get; set; }
public string OrderID { get; set; }
public string Email { get; set; }
public string ItemNumber { get; set; }
public int Quantity { get; set; }
public Order(string[] ordered , string[] items)
{
Type = ordered[0];
Date = DateTime.Parse(ordered[1]);
OrderID = ordered[2];
Email = ordered[3];
Type = items[0];
OrderID = items[1];
ItemNumber = items[2];
Quantity = int.Parse(items[3]);
}
}
class Program
{
static List<Order> orders = new List<Order>();
static void Main(string[] args)
{
Reading();
}
private static void Reading()
{
using (System.IO.StreamReader reader = new System.IO.StreamReader("orders.csv"))
{
while (!reader.EndOfStream)
{
orders.Add(new Order(reader.ReadLine().Split(';') , reader.ReadLine().Split(';')));
}
}
}
}
You can try to identify the line before creating it.
Than you can create two different methods to initialize your order.
while (!reader.EndOfStream)
{
var values = reader.ReadLine().Split(';');
if(DateTime.TryParse(values.Skip(1).First(), out var date)) {
orders.Add(Order.FromOrderWithDate(values));
}
else
orders.Last().Items.Add(Item.FromOrderWithEmail(values));
}
The two methods will be something like
public static Order FromRow(string[] ordered) =>
new Order {
Type = ordered[0],
Date = DateTime.Parse(ordered[1]),
OrderID = ordered[2],
Email = ordered[3],
Items = new List<Item>();
};
public static Item FromRow(string[] items) =>
new Item {
Type = items[0],
OrderID = items[1],
ItemNumber = items[2],
Quantity = int.Parse(items[3])
};
And finally two different class, one for order and one for item, the Order should contain a list for the items.
Try something like:
List<Customer> customers = new List<Customer>();
Customer lastCustomer = null;
foreach(var line in File.ReadLines("orders.csv"))
{
var values = line.Split(';');
if (values[0]=="M")
{
lastCustomer = new Customer(values);
customes.Add(lastCustomer);
}
else if (values[0]=="T" && lastCustomer != null)
{
lastCustomer.AddOrder(values);
}
}
(you'll need to write a Customer class that can construct its self from an array of strings, plus has a method for adding new Order objects to its own list of orders, again constructing them from an array)
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.
Let me explain, I have a model list in which I have a little more than a thousand parameters, so I have to fill the list with some variables, the thing is that I don't want to do this:
list.Add(new Model{
name1= value,
name2= value,
.....
name1000=value
});
I have an array that contains the names of the parameters in the list, so I was wondering if is possible to use that array of the names and in a loop get the variables fill in, something like this:
list.Add(new Model{
//a loop?
array[0]= value
});
Thanks.
You can achieve this using reflection. Code below
public class ModelFactory
{
private IDictionary<string, PropertyInfo> propertiesInfo { get; set; }
public ModelFactory()
{
this.propertiesInfo = typeof(Model)
.GetProperties()
.ToDictionary(p => p.Name, p => p);
}
public Model Create(string[] propertiesToInitialize, dynamic value)
{
var model = new Model();
foreach (var propertyName in propertiesToInitialize)
{
if (this.propertiesInfo.ContainsKey(propertyName))
{
var property = this.propertiesInfo[propertyName];
property.SetValue(model, value);
}
}
return model;
}
}
Model to initialize
public class Model
{
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
public int MyProperty4 { get; set; }
public int MyProperty5 { get; set; }
}
Usage
public void Test()
{
var propertiesToInitialize = new string[] { "MyProperty1", "MyProperty2", "MyProperty4" };
var modelFactory = new ModelFactory();
var list = new List<Model>();
list.Add(modelFactory.Create(propertiesToInitialize, 500));
Console.WriteLine("MyProperty1 " + list[0].MyProperty1); // 500
Console.WriteLine("MyProperty2 " + list[0].MyProperty2); // 500
Console.WriteLine("MyProperty3 " + list[0].MyProperty3); // 0
Console.WriteLine("MyProperty4 " + list[0].MyProperty4); // 500
Console.WriteLine("MyProperty5 " + list[0].MyProperty5); // 0
}
However as already mentioned in comments, please reconsider your model design because model with these many properties is not optimal.
I've been working on using reflection but its very new to me still. So the line below works. It returns a list of DataBlockOne
var endResult =(List<DataBlockOne>)allData.GetType()
.GetProperty("One")
.GetValue(allData);
But I don't know myType until run time. So my thoughts were the below code to get the type from the object returned and cast that type as a list of DataBlockOne.
List<DataBlockOne> one = new List<DataBlockOne>();
one.Add(new DataBlockOne { id = 1 });
List<DataBlockTwo> two = new List<DataBlockTwo>();
two.Add(new DataBlockTwo { id = 2 });
AllData allData = new AllData
{
One = one,
Two = two
};
var result = allData.GetType().GetProperty("One").GetValue(allData);
Type thisType = result.GetType().GetGenericArguments().Single();
Note I don't know the list type below. I just used DataBlockOne as an example
var endResult =(List<DataBlockOne>)allData.GetType() // this could be List<DataBlockTwo> as well as List<DataBlockOne>
.GetProperty("One")
.GetValue(allData);
I need to cast so I can search the list later (this will error if you don't cast the returned object)
if (endResult.Count > 0)
{
var search = endResult.Where(whereExpression);
}
I'm confusing the class Type and the type used in list. Can someone point me in the right direction to get a type at run time and set that as my type for a list?
Class definition:
public class AllData
{
public List<DataBlockOne> One { get; set; }
public List<DataBlockTwo> Two { get; set; }
}
public class DataBlockOne
{
public int id { get; set; }
}
public class DataBlockTwo
{
public int id { get; set; }
}
You might need something like this:
var endResult = Convert.ChangeType(allData.GetType().GetProperty("One").GetValue(allData), allData.GetType());
Just guessing, didn't work in C# since 2013, please don't shoot :)
You probably want something like this:
static void Main(string[] args)
{
var one = new List<DataBlockBase>();
one.Add(new DataBlockOne { Id = 1, CustomPropertyDataBlockOne = 314 });
var two = new List<DataBlockBase>();
two.Add(new DataBlockTwo { Id = 2, CustomPropertyDatablockTwo = long.MaxValue });
AllData allData = new AllData
{
One = one,
Two = two
};
#region Access Base Class Properties
var result = (DataBlockBase)allData.GetType().GetProperty("One").GetValue(allData);
var oneId = result.Id;
#endregion
#region Switch Into Custom Class Properties
if (result is DataBlockTwo)
{
var thisId = result.Id;
var thisCustomPropertyTwo = ((DataBlockTwo)result).CustomPropertyDatablockTwo;
}
if (result is DataBlockOne)
{
var thisId = result.Id;
var thisCustomPropertyOne = ((DataBlockOne)result).CustomPropertyDataBlockOne;
}
#endregion
Console.Read();
}
public class AllData
{
public List<DataBlockBase> One { get; set; }
public List<DataBlockBase> Two { get; set; }
}
public class DataBlockOne : DataBlockBase
{
public int CustomPropertyDataBlockOne { get; set; }
}
public class DataBlockTwo : DataBlockBase
{
public long CustomPropertyDatablockTwo { get; set; }
}
public abstract class DataBlockBase
{
public int Id { get; set; }
}
I have the following classes:
public class Products
{
public int Id { get; set; }
public string ProductName { get; set; }
public int Price { get; set; }
public IList<ProductFiles> ProductFiles { get; set; }
}
public class ProductFiles
{
public int NumberOfFiles { get; set; }
public int NumberOfShops { get; set; }
}
Here I am trying to set the value of the property NumberOfFiles (member of ProductFiles):
public Products CountProductFiles(int productId)
{
DB_utilities db = new DB_utilities();
object[] spParams = new object[] { productId};
Products product = new Products();
using (var reader = db.procSelect("[Products_CountFiles]", spParams))
{
reader.Read();
{
Products _products = new Products
{
ProductName = (string)reader["ProductName"],
Price = (double)reader["Price"],
// I am trying to do something like this but this does not work:
ProductFiles.NumberOfFiles = (int)reader["NumberOfFiles"]
};
Products = _products ;
}
}
return Products;
}
How can I set the value of the prop NumberOfFiles? Or is the entire concept wrong?
As ProductFiles is a collection of ProductFiles, you need to do this
Products p = new Products();
p.ProductFiles = new List<ProductFiles>();
p.ProductFiles.Add(new ProductFiles() { NumberOfFiles = 1 }); // or some other defined value.
The collection needs to be initialized before adding objects to it.
In your example, you do not treat ProductFiles as a collection, that is why you get the error.
The point of a collection is that it can contain zero or more items. You need to put an instance in the collection. You also need to create a collection to ensure that the collection itself is not null.
new Product {
Files = new List<ProductFile>() {
new ProductFile {
FileCount = 42
}
}
}
In this example, Products::ProductFiles is a list.
Hence, in order to set the value of NumberOfFiles, do something like this:
ProductFiles[i].NumberOfFiles = //whatever you want to set it to.
But, before doing all this, do not forget to instantiate the List variable. Otherwise, it will itself be NULL.