Reputation: 9295
I need to fill a list with specific data. This list is a property of another object. The elements of that List have the following rules:
This is the code so far: (I did not add any plausiblity checks or error handling to keep this example simple)
private void SetListProperty(string propertyName, object target, int[] ids) {
PropertyInfo property=target.GetProperty(propertyName);
Type propertyType=property.PropertyType();
Type elementType=propertyType.GetElementType();
PropertyInfo elementId=elementType.GetProperty("Id");
var targetList=new List<>(elementType); // PseudoCode. Does not work
foreach (int id in ids) {
var element=Activator.CreateInstance(elementType);
elementId.SetValue(element, id);
targetList.Add(element); // PseudoCode. Does not work
}
property.SetValue(target, targetList);
}
Example for calling that method:
public class User {
public int Id {get;set;}
public string Name {get;set;}
}
public class House {
public int Id {get;set;}
public string HouseName {get;set;] }
public class Group {
public string Name {get;set;}
public List<User> Users {get;set;}
public List<House> Houses {get;set;}
}
var group=new Group { Name="nerds"};
SetListProperty("Users"), group, new int[] {1,2,3,4,5});
SetListProperty("Houses"), group, new int[] {1,2,3,4,5});
So after calling that method, group
should contain a property Users
that has 5 elements each with the Id set.
I've seen similar questions here about creating a List of an unknown Type, but not how to actually add single items of an unknown type to that list.
(Update) I assume my problem was not clear enough in the beginning. Inside that method I do NOT know the property Type. Not even if it is a list.
I only have the property name as string.
Upvotes: 2
Views: 1243
Reputation: 5692
I scrapped the original answer (below) to truly address doing this without knowing the type of property OTHER than that it is either an item that has an id...or it is an enumerable of objects that have an Id.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace StackOverflowTests
{
[TestClass]
public class StackOverflow_49181925Tests
{
public interface IHasId
{
int Id { get; set; }
}
public class Foo : IHasId
{
public int Id { get; set; }
}
public class HasAFoo
{
public Foo Foo { get; set; }
}
public class HasManyFoos
{
public IEnumerable<Foo> Foos { get; set; }
}
public void SetPropertyIds(object target, string propertyName, IEnumerable<int> ids)
{
var property = target.GetType().GetProperty(propertyName);
var propertyType = property.PropertyType;
//is it enumerable?
if (typeof(IEnumerable).IsAssignableFrom(propertyType))
{
var objectType = propertyType.GetGenericArguments().First();
var list = Activator.CreateInstance(typeof(List<>).MakeGenericType(objectType)) as IList;
foreach(var id in ids)
{
var obj = Activator.CreateInstance(objectType) as IHasId;
((IHasId)obj).Id = id;
list.Add(obj);
}
property.SetValue(target, list);
}
else
{
if(ids.Count() != 1) throw new ApplicationException("You're trying to set multiple Ids to a single property.");
var objectType = propertyType;
var obj = Activator.CreateInstance(objectType);
((IHasId)obj).Id = ids.First();
property.SetValue(target, obj);
}
}
[TestMethod]
public void TestHasAFoo()
{
var target = new HasAFoo();
this.SetPropertyIds(target, "Foo", new[] { 1 });
Assert.AreEqual(target.Foo.Id, 1);
}
[TestMethod]
public void TestHasManyFoos()
{
var target = new HasManyFoos();
this.SetPropertyIds(target, "Foos", new[] { 1, 2 });
Assert.AreEqual(target.Foos.ElementAt(0).Id, 1);
Assert.AreEqual(target.Foos.ElementAt(1).Id, 2);
}
}
}
ORIGINAL ANSWER BELOW
Lots of different ways of accomplishing this. The third way which implements an interface can give you a lot of help since most of your business models probably have some sort of Id. The Linq version though is short and sweet. These are all just variations on a theme.
Using Linq:
group.Users = new[]{1,2,3}.Select(s=>new User{Id = s}).ToList();
Using Generics:
private IList<T> IntsToModels<T>(IEnumerable<int> ints, Action<T,int> setId) where T : new(){
return ints.Select(s=>{
var t = new T();
setId(t,s);
return t;
}).ToList();
}
Using Generics and Interfaces
public interface IHasID{
int Id{get;set;}
}
//implement the IHasID interface
public class User : IHasID...
private IEnumerable<T> ToModels(IEnumerable<int> ints) where T : IHasID, new(){
foreach(var i in ints){
yield return new T{Id = i};
}
}
Upvotes: 4