I have a List<> of custom objects.
I need to find an object in this list by some property which is unique and update another property of this object.
What is the quickest way to do it?
Using Linq to find the object you can do:
var obj = myList.FirstOrDefault(x => x.MyProperty == myValue);
if (obj != null) obj.OtherProperty = newValue;
But in this case you might want to save the List into a Dictionary and use this instead:
// ... define after getting the List/Enumerable/whatever
var dict = myList.ToDictionary(x => x.MyProperty);
// ... somewhere in code
MyObject found;
if (dict.TryGetValue(myValue, out found)) found.OtherProperty = newValue;
Just to add to CKoenig's response. His answer will work as long as the class you're dealing with is a reference type (like a class). If the custom object were a struct, this is a value type, and the results of .FirstOrDefault will give you a local copy of that, which will mean it won't persist back to the collection, as this example shows:
struct MyStruct
{
public int TheValue { get; set; }
}
Test code:
List<MyStruct> coll = new List<MyStruct> {
new MyStruct {TheValue = 10},
new MyStruct {TheValue = 1},
new MyStruct {TheValue = 145},
};
var found = coll.FirstOrDefault(c => c.TheValue == 1);
found.TheValue = 12;
foreach (var myStruct in coll)
{
Console.WriteLine(myStruct.TheValue);
}
Console.ReadLine();
The output is 10,1,145
Change the struct to a class and the output is 10,12,145
HTH
or without linq
foreach(MyObject obj in myList)
{
if(obj.prop == someValue)
{
obj.otherProp = newValue;
break;
}
}
Can also try.
_lstProductDetail.Where(S => S.ProductID == "")
.Select(S => { S.ProductPcs = "Update Value" ; return S; }).ToList();
You can do somthing like :
if (product != null) {
var products = Repository.Products;
var indexOf = products.IndexOf(products.Find(p => p.Id == product.Id));
Repository.Products[indexOf] = product;
// or
Repository.Products[indexOf].prop = product.prop;
}
var itemIndex = listObject.FindIndex(x => x == SomeSpecialCondition());
var item = listObject.ElementAt(itemIndex);
item.SomePropYouWantToChange = "yourNewValue";
This was a new discovery today - after having learned the class/struct reference lesson!
You can use Linq and "Single" if you know the item will be found, because Single returns a variable...
myList.Single(x => x.MyProperty == myValue).OtherProperty = newValue;
I found a way of doing it in one Line of code unsing LINQ:
yourList.Where(yourObject => yourObject.property == "yourSearchProperty").Select(yourObject => { yourObject.secondProperty = "yourNewProperty"; return yourObject; }).ToList();
var index = yourList.FindIndex(x => x.yourProperty == externalProperty);
if (index > -1)
{
yourList[index] = yourNewObject;
}
yourlist now has the updated object inside of it.
It is also worth mentioning that what Matt Roberts said applies to structs inside classes.
So even when you have a class (which will pass by reference, theoretically), a property which is a list of structs inside it will pass by value when you try to find it and change a value inside a given struct (on the list).
For instance (using the same code as Matt proposed):
ParentClass instance = new ParentClass();
var found = instance.ListofStruct.FirstOrDefault(c => c.TheValue == 1);
found.TheValue = 12;
foreach (var myStruct in instance.ListofStruct)
{
Console.WriteLine(myStruct.TheValue);
}
Console.ReadLine();
public class ParentClass
{
public List<MyStruct> ListofStruct { get; set; }
public struct MyStruct
{
public int TheValue { get; set; }
}
public ParentClass()
{
ListofStruct = new List<MyStruct>()
{
new MyStruct {TheValue = 10},
new MyStruct {TheValue = 1},
new MyStruct {TheValue = 145}
};
}
}
Will output: 10, 1, 145
Whereas changing the struct (inside the ParentClass) to a class, like this:
public class ParentClass
{
public List<MySubClass> ListofClass { get; set; }
public class MySubClass
{
public int TheValue { get; set; }
}
public ParentClass()
{
ListofClass = new List<MySubClass>()
{
new MySubClass {TheValue = 10},
new MySubClass {TheValue = 1},
new MySubClass {TheValue = 145}
};
}
}
Will output: 10, 12, 145
//Find whether the element present in the existing list
if (myList.Any(x => x.key == "apple"))
{
//Get that Item
var item = myList.FirstOrDefault(x => x.key == ol."apple");
//update that item
item.Qty = "your new value";
}
Related
I have a Generic List which can contain different class objects. Al these classes have the same base class which has a Id property.
I would like to do a search for the Id in the List and extract the first occourence, something like this
T result = myGenericList.Where(i => i.Id == id).FirstOrDefault()
However I cannot get to the properties, I only get as far as: i => i.
EDIT:
I'm asked to provide the code for the myGenericList. Here it goes:
public static IEnumerable<T> Initialize()
{
List<T> allCalls = new List<T>();
var phoneCalls = new PhoneCall[]
{
new PhoneCall{Id = 1, Date= DateTime.Now.AddDays(-1), DurationSec = 60, Phone = "", Region = new Country { From = "", To = ""}},
...
};
foreach (PhoneCall s in phoneCalls)
{
allCalls.Add(s as T);
}
var dataCalls = new DataCall[]
{
new DataCall{Id = 6, WebData = 5120, Phone = "", Region = new Country { From = "", To = ""}},
...
};
foreach (DataCall s in dataCalls)
{
allCalls.Add(s as T);
}
var smsCalls = new SmsCall[]
{
new SmsCall{Id = 6, SmsData = 512, Phone = "", Region = new Country { From = "", To = ""}},
...
};
foreach (SmsCall s in smsCalls)
{
allCalls.Add(s as T);
}
return allCalls;
Regarding the T that is a class which I have defined at the top of my Repository.cs class
public class Repository<T> : IRepository<T> where T : class
What you should do is define what T is and not cast the items to T. As the compiler has no way to know what T is it does not know it has the Id property. Use a generic constraint to do so:
public static IEnumerable<T> Initialize<T where T : BaseClass>()
{
var allCalls = new List<T>();
allCalls.Add(new PhoneCall { /* Details */};
return allCalls;
}
After question update - move the constraint from being one of the function to being one of the class
Also you can use the FirstOrDefault overload that gets a predicate:
var result = myGenericList.FirstOrDefault(i => i.Id == id);
Have you tried casting to the base class?
myGenericList
.Cast<BaseClass>()
.FirstOrDedault(i => i.Id === id)
Or alternatively
myGenericList.FirstOrDefault(i => (i as BaseClass).Id === id)
i am getting properties via reflection and i was doing like this to iterate on the list.
private string HandleListProperty(object oldObject, object newObject, string difference, PropertyInfo prop)
{
var oldList = prop.GetValue(oldObject, null) as IList;
var newList = prop.GetValue(newObject, null) as IList;
if (prop.PropertyType == typeof(List<DataModel.ScheduleDetail>))
{
List<DataModel.ScheduleDetail> ScheduleDetailsOld = oldList as List<DataModel.ScheduleDetail>;
List<DataModel.ScheduleDetail> ScheduleDetailsNew = newList as List<DataModel.ScheduleDetail>;
var groupOldSchedules = ScheduleDetailsOld
.GroupBy(x => x.HomeHelpID)
.SelectMany(s => s.DistinctBy(d => d.HomeHelpID)
.Select(h => new { h.HomeHelpID, h.HomeHelpName }));
var groupNewSchedules = ScheduleDetailsNew
.GroupBy(x => x.HomeHelpID)
.SelectMany(s => s.DistinctBy(d => d.HomeHelpID)
.Select(h => new { h.HomeHelpID, h.HomeHelpName }));
var AddedHomeHelp = string.Join(",", groupNewSchedules
.Where(x => x.HomeHelpID != null && !groupOldSchedule
.Any(y => y.HomeHelpID == x.HomeHelpID))
.Select(x => "\"<strong>" + x.HomeHelpName + "\"</strong>"));
var RemovedHomeHelp = string.Join(",", groupOldSchedules
.Where(x => x.HomeHelpID != null && groupNewSchedules
.Any(y => y.HomeHelpID != x.HomeHelpID))
.Select(x => "\"<strong>"+x.HomeHelpName+"\"</strong>"));
difference += string.IsNullOrWhiteSpace(RemovedHomeHelp) ? string.Empty : "<strong>HomeHelp</strong> " + RemovedHomeHelp + " Removed<br/>";
difference += string.IsNullOrWhiteSpace(AddedHomeHelp) ? string.Empty : "<strong>HomeHelp</strong> " + AddedHomeHelp + "Added<br/>";
}
}
Now i am making it generic because there will be coming different types of Lists and i don't want to put if conditions this way i want to write
generic code to handle any type of list.
I came up with this way:
private void HandleListProperty(object oldObject, object newObject, string difference, PropertyInfo prop)
{
var oldList = prop.GetValue(oldObject, null) as IList;
var newList = prop.GetValue(newObject, null) as IList;
var ListType = prop.PropertyType;
var MyListInstance = Activator.CreateInstance(ListType);
MyListInstance = oldList;
}
i am able to get the items in MyListInstance but as the type will come at runtime i am not getting how to write linq query to filter them, any idea how to do.
You want to compare lists. As I understand it, the 2 lists you compare will always be of the same type but one time it might be 2 lists of X and another time it might be 2 lists of Y.
The LINQ Except method is great for this. By default it compares if the items are the same exact reference but with a custom comparer, it can compare based in the ID property or anything else you want.
You need to determine how you consider 2 items to be equal and create a custom comparer that implements IEqualityComparer<T> like below.
public class CompareSchedules : IEqualityComparer<ScheduleDetail>
{
public bool Equals(ScheduleDetail x, ScheduleDetail y)
{
return x.HomeHelpID == y.HomeHelpID;
}
public int GetHashCode(ScheduleDetail obj)
{
return obj.HomeHelpID;
}
}
public static class SuperDuperListComparer
{
public class ListCompareResults<T>
{
public List<T> RemovedItems { get; set; }
public List<T> AddedItems { get; set; }
}
public static ListCompareResults<T> CompareLists<T>(IList<T> list1, IList<T> list2, IEqualityComparer<T> comparer)
{
var addedItems = list2.Except(list1, comparer).ToList();
var removedItems = list1.Except(list2, comparer).ToList();
return new ListCompareResults<T>
{
AddedItems = addedItems,
RemovedItems = removedItems
};
}
public static ListCompareResults<T> CompareLists<T>(IList<T> list1, IList<T> list2)
{
return CompareLists<T>(list1, list2, EqualityComparer<T>.Default);
}
}
and here is some example code.
[TestMethod]
public void TestListComparer()
{
var list1 = new List<ScheduleDetail> {
new ScheduleDetail { HomeHelpID = 1},
new ScheduleDetail { HomeHelpID = 3}
};
var list2 = new List<ScheduleDetail> {
new ScheduleDetail { HomeHelpID = 1},
new ScheduleDetail { HomeHelpID = 5}
};
var comparison = SuperDuperListComparer.CompareLists(list1, list2, new CompareSchedules());
}
public class ScheduleDetail
{
public int HomeHelpID { get; set; }
}
I want to get the names of all properties that changed for matching objects. I have these (simplified) classes:
public enum PersonType { Student, Professor, Employee }
class Person {
public string Name { get; set; }
public PersonType Type { get; set; }
}
class Student : Person {
public string MatriculationNumber { get; set; }
}
class Subject {
public string Name { get; set; }
public int WeeklyHours { get; set; }
}
class Professor : Person {
public List<Subject> Subjects { get; set; }
}
Now I want to get the objects where the Property values differ:
List<Person> oldPersonList = ...
List<Person> newPersonList = ...
List<Difference> = GetDifferences(oldPersonList, newPersonList);
public List<Difference> GetDifferences(List<Person> oldP, List<Person> newP) {
//how to check the properties without casting and checking
//for each type and individual property??
//can this be done with Reflection even in Lists??
}
In the end I would like to have a list of Differences like this:
class Difference {
public List<string> ChangedProperties { get; set; }
public Person NewPerson { get; set; }
public Person OldPerson { get; set; }
}
The ChangedProperties should contain the name of the changed properties.
I've spent quite a while trying to write a faster reflection-based solution using typed delegates. But eventually I gave up and switched to Marc Gravell's Fast-Member library to achieve higher performance than with normal reflection.
Code:
internal class PropertyComparer
{
public static IEnumerable<Difference<T>> GetDifferences<T>(PropertyComparer pc,
IEnumerable<T> oldPersons,
IEnumerable<T> newPersons)
where T : Person
{
Dictionary<string, T> newPersonMap = newPersons.ToDictionary(p => p.Name, p => p);
foreach (T op in oldPersons)
{
// match items from the two lists by the 'Name' property
if (newPersonMap.ContainsKey(op.Name))
{
T np = newPersonMap[op.Name];
Difference<T> diff = pc.SearchDifferences(op, np);
if (diff != null)
{
yield return diff;
}
}
}
}
private Difference<T> SearchDifferences<T>(T obj1, T obj2)
{
CacheObject(obj1);
CacheObject(obj2);
return SimpleSearch(obj1, obj2);
}
private Difference<T> SimpleSearch<T>(T obj1, T obj2)
{
Difference<T> diff = new Difference<T>
{
ChangedProperties = new List<string>(),
OldPerson = obj1,
NewPerson = obj2
};
ObjectAccessor obj1Getter = ObjectAccessor.Create(obj1);
ObjectAccessor obj2Getter = ObjectAccessor.Create(obj2);
var propertyList = _propertyCache[obj1.GetType()];
// find the common properties if types differ
if (obj1.GetType() != obj2.GetType())
{
propertyList = propertyList.Intersect(_propertyCache[obj2.GetType()]).ToList();
}
foreach (string propName in propertyList)
{
// fetch the property value via the ObjectAccessor
if (!obj1Getter[propName].Equals(obj2Getter[propName]))
{
diff.ChangedProperties.Add(propName);
}
}
return diff.ChangedProperties.Count > 0 ? diff : null;
}
// cache for the expensive reflections calls
private Dictionary<Type, List<string>> _propertyCache = new Dictionary<Type, List<string>>();
private void CacheObject<T>(T obj)
{
if (!_propertyCache.ContainsKey(obj.GetType()))
{
_propertyCache[obj.GetType()] = new List<string>();
_propertyCache[obj.GetType()].AddRange(obj.GetType().GetProperties().Select(pi => pi.Name));
}
}
}
Usage:
PropertyComparer pc = new PropertyComparer();
var diffs = PropertyComparer.GetDifferences(pc, oldPersonList, newPersonList).ToList();
Performance:
My very biased measurements showed that this approach is about 4-6 times faster than the Json-Conversion and about 9 times faster than ordinary reflections. But in fairness, you could probably speed up the other solutions quite a bit.
Limitations:
At the moment my solution doesn't recurse over nested lists, for example it doesn't compare individual Subject items - it only detects that the subjects lists are different, but not what or where. However, it shouldn't be too hard to add this feature when you need it. The most difficult part would probably be to decide how to represent these differences in the Difference class.
We start with 2 simple methods:
public bool AreEqual(object leftValue, object rightValue)
{
var left = JsonConvert.SerializeObject(leftValue);
var right = JsonConvert.SerializeObject(rightValue);
return left == right;
}
public Difference<T> GetDifference<T>(T newItem, T oldItem)
{
var properties = typeof(T).GetProperties();
var propertyValues = properties
.Select(p => new {
p.Name,
LeftValue = p.GetValue(newItem),
RightValue = p.GetValue(oldItem)
});
var differences = propertyValues
.Where(p => !AreEqual(p.LeftValue, p.RightValue))
.Select(p => p.Name)
.ToList();
return new Difference<T>
{
ChangedProperties = differences,
NewItem = newItem,
OldItem = oldItem
};
}
AreEqual just compares the serialized versions of two objects using Json.Net, this keeps it from treating reference types and value types differently.
GetDifference checks the properties on the passed in objects and compares them individually.
To get a list of differences:
var oldPersonList = new List<Person> {
new Person { Name = "Bill" },
new Person { Name = "Bob" }
};
var newPersonList = new List<Person> {
new Person { Name = "Bill" },
new Person { Name = "Bobby" }
};
var diffList = oldPersonList.Zip(newPersonList, GetDifference)
.Where(d => d.ChangedProperties.Any())
.ToList();
Everyone always tries to get fancy and write these overly generic ways of extracting data. There is a cost to that.
Why not be old school simple.
Have a GetDifferences member function Person.
virtual List<String> GetDifferences(Person otherPerson){
var diffs = new List<string>();
if(this.X != otherPerson.X) diffs.add("X");
....
}
In inherited classes. Override and add their specific properties. AddRange the base function.
KISS - Keep it simple. It would take you 10 minutes of monkey work to write it, and you know it will be efficient and work.
I am doing it by using this:
//This structure represents the comparison of one member of an object to the corresponding member of another object.
public struct MemberComparison
{
public static PropertyInfo NullProperty = null; //used for ROOT properties - i dont know their name only that they are changed
public readonly MemberInfo Member; //Which member this Comparison compares
public readonly object Value1, Value2;//The values of each object's respective member
public MemberComparison(PropertyInfo member, object value1, object value2)
{
Member = member;
Value1 = value1;
Value2 = value2;
}
public override string ToString()
{
return Member.name+ ": " + Value1.ToString() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToString();
}
}
//This method can be used to get a list of MemberComparison values that represent the fields and/or properties that differ between the two objects.
public static List<MemberComparison> ReflectiveCompare<T>(T x, T y)
{
List<MemberComparison> list = new List<MemberComparison>();//The list to be returned
if (x.GetType().IsArray)
{
Array xArray = x as Array;
Array yArray = y as Array;
if (xArray.Length != yArray.Length)
list.Add(new MemberComparison(MemberComparison.NullProperty, "array", "array"));
else
{
for (int i = 0; i < xArray.Length; i++)
{
var compare = ReflectiveCompare(xArray.GetValue(i), yArray.GetValue(i));
if (compare.Count > 0)
list.AddRange(compare);
}
}
}
else
{
foreach (PropertyInfo m in x.GetType().GetProperties())
//Only look at fields and properties.
//This could be changed to include methods, but you'd have to get values to pass to the methods you want to compare
if (!m.PropertyType.IsArray && (m.PropertyType == typeof(String) || m.PropertyType == typeof(double) || m.PropertyType == typeof(int) || m.PropertyType == typeof(uint) || m.PropertyType == typeof(float)))
{
var xValue = m.GetValue(x, null);
var yValue = m.GetValue(y, null);
if (!object.Equals(yValue, xValue))//Add a new comparison to the list if the value of the member defined on 'x' isn't equal to the value of the member defined on 'y'.
list.Add(new MemberComparison(m, yValue, xValue));
}
else if (m.PropertyType.IsArray)
{
Array xArray = m.GetValue(x, null) as Array;
Array yArray = m.GetValue(y, null) as Array;
if (xArray.Length != yArray.Length)
list.Add(new MemberComparison(m, "array", "array"));
else
{
for (int i = 0; i < xArray.Length; i++)
{
var compare = ReflectiveCompare(xArray.GetValue(i), yArray.GetValue(i));
if (compare.Count > 0)
list.AddRange(compare);
}
}
}
else if (m.PropertyType.IsClass)
{
var xValue = m.GetValue(x, null);
var yValue = m.GetValue(y, null);
if ((xValue == null || yValue == null) && !(yValue == null && xValue == null))
list.Add(new MemberComparison(m, xValue, yValue));
else if (!(xValue == null || yValue == null))
{
var compare = ReflectiveCompare(m.GetValue(x, null), m.GetValue(y, null));
if (compare.Count > 0)
list.AddRange(compare);
}
}
}
return list;
}
Here you have a code which does what you want with Reflection.
public List<Difference> GetDifferences(List<Person> oldP, List<Person> newP)
{
List<Difference> allDiffs = new List<Difference>();
foreach (Person oldPerson in oldP)
{
foreach (Person newPerson in newP)
{
Difference curDiff = GetDifferencesTwoPersons(oldPerson, newPerson);
allDiffs.Add(curDiff);
}
}
return allDiffs;
}
private Difference GetDifferencesTwoPersons(Person OldPerson, Person NewPerson)
{
MemberInfo[] members = typeof(Person).GetMembers();
Difference returnDiff = new Difference();
returnDiff.NewPerson = NewPerson;
returnDiff.OldPerson = OldPerson;
returnDiff.ChangedProperties = new List<string>();
foreach (MemberInfo member in members)
{
if (member.MemberType == MemberTypes.Property)
{
if (typeof(Person).GetProperty(member.Name).GetValue(NewPerson, null).ToString() != typeof(Person).GetProperty(member.Name).GetValue(OldPerson, null).ToString())
{
returnDiff.ChangedProperties.Add(member.Name);
}
}
}
return returnDiff;
}
I try to find the way for refactoring my code but no idea how to do this.
For example, we have several classes
class A {
string name;
int year;
}
class B {
long id;
string code;
DateTime currentTime;
}
class C {
string lastname;
DateTime currentDate;
}
And latter I need to return list of objects of these classes List, List, List and convert them into Object[][].
For every conversion I do the same
private Object[][] converAListToObjectArray(List<A> dataList)
{
long countRecords = dataList.Count();
const int countProperty = 2;
var arrayRes = new object[countRecords][];
for (int i = 0; i < countRecords; i++)
{
var arrayObjProperty = new object[countProperty];
arrayObjProperty[0] = dataList[i].Name;
arrayObjProperty[1] = dataList[i].Year;
arrayRes[i] = arrayObjProperty;
}
return arrayRes;
}
private Object[][] converBListToObjectArray(List<B> dataList)
{
long countRecords = dataList.Count();
const int countProperty = 3;
var arrayRes = new object[countRecords][];
for (int i = 0; i < countRecords; i++)
{
var arrayObjProperty = new object[countProperty];
arrayObjProperty[0] = dataList[i].Id;
arrayObjProperty[1] = dataList[i].Code;
arrayObjProperty[2] = dataList[i].CurrentTime;
arrayRes[i] = arrayObjProperty;
}
return arrayRes;
}
Is it possible separate this convertion using some design pattern?
You could write a generic function which uses reflection to get each object's field names and values. You'd need to decide whether it should work for public or private fields. The example below grabs both the public and the private fields:
static object[][] ConvertToObjectArray<T>(IList<T> objects)
{
var fields = (from fieldInfo in typeof(T).GetFields(
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
orderby fieldInfo.Name
select fieldInfo).ToArray();
object[][] table = new object[objects.Count][];
for (int i = 0; i < table.Length; i++)
{
table[i] = (from fieldInfo in fields
select fieldInfo.GetValue(objects[i])).ToArray();
}
return table;
}
You could use an action and do something like this: -
class Program
{
// Your new function, (doesn't have to be static; just did it for the demo)
// If you really really want to return object[][] still,
// You'll need to pass an index to foo as well
private static List<IList<object>> convert<T>(IList<T> dataList, Action<IList<object>, T> foo)
{
var arrayRes = new List<IList<object>>();
foreach (var item in dataList)
{
var arrayObjProperty = new List<object>();
foo(arrayObjProperty, item);
arrayRes.Add(arrayObjProperty);
}
return arrayRes;
}
// The rest is just calling the function with two examples
static void Main(string[] args)
{
var bar = new List<A>();
bar.Add(new A() { name = "qux", year = 2013 });
var objects1 = convert(bar, (a, b) =>
{
a.Add(b.name);
a.Add(b.year);
});
var baz = new List<B>();
baz.Add(new B() { code = "qux", id = 2013 });
var objects2 = convert(baz, (a, b) =>
{
a.Add(b.code);
a.Add(b.id);
});
}
}
You can just copy this into your IDE to have a play and see how it works. Basically, this uses generics and then an action to allow you to do the only part that differs each time in a lambda that is passed to the method.
You could simplify and use something like this - e.g. for B...
List<B> list = new List<B>
{
new B{ id = 1, code = "", currentTime = DateTime.Now},
new B{ id = 1, code = "", currentTime = DateTime.Now},
new B{ id = 1, code = "", currentTime = DateTime.Now},
};
var array = list.Select(x => new object[] { x.id, x.code, x.currentTime }).ToArray();
Your Object[][] structure is a little unusual to my eye. Did you know there's a Cast method for lists?
You can do the following:
List<A> foo = new List<A> ();
// initialize foo here
var bar = foo.Cast<Object>().ToArray();
Which will get you a an array of objects with your field names intact. If you absolutely need to have the fields as array elements (why do you want this) you could add a ToObject method to each of your classes:
class A
{
public string name;
public int year;
public Object[] ToObject()
{
return new Object[] {name, year};
}
}
List<A> foo = new List<A>
{
new A{name="reacher",year=2013},
new A{name="Ray",year=2013}
};
var bux = foo.Select(a => a.ToObject()).ToArray() ;
I have the a class Foo like this:
class Foo
{
public int id{get;set;}
public IEnumerable<Foo> Childs;
//some other properties
}
Now I want to process some business logic on a Foo-Object and all it's children like this:
public void DoSomeWorkWith(Foo x)
{
var firstItem = new {level = 0, item = x};
var s = new Stack<?>(?); //What type to use?
s.Push(firstItem);
while(s.Any())
{
var current = s.Pop();
DoSomeBusiness(current.item);
DoSomeMoreBusiness(current.item);
Log(current.level, current.item.id);
foreach(Foo child in current.item.Childs)
s.Push(new {level = current.level + 1, item = child});
}
}
I need to keep track of the (relative) level/depth of the childs.
How do I create a Stack<T> for the anonymous type? Of course I could create a simple class instead of the anonymous type (or a more complicated recursive function), but how to solve this problem without an additional class?
How about:
public static Stack<T> CreateEmptyStack<T>(T template) {
return new Stack<T>();
}
...
var stack = CreateEmptyStack(firstItem);
This uses generic type inference to handle the T.
You could just put it into a method like this:
public Stack<T> CreateStackWithInitialItem<T>(T initialItem)
{
var s = new Stack<T>();
s.Push(initialItem);
return s;
}
and then use it like that:
public void DoSomeWorkWith(Foo x)
{
var s = CreateStackWithInitialItem(new {level = 0, item = x});
while(s.Any())
{
...
}
}
What about using tuples (System.Tuple<>) instead of anonymous types?
public void DoSomeWorkWith(Foo x)
{
var firstItem = new Tuple<int, Foo>(0, x);
var s = new Stack<Tuple<int, Foo>>();
s.Push(firstItem);
while (s.Any())
{
var current = s.Pop();
DoSomeBusiness(current.Item2);
DoSomeMoreBusiness(current.Item2);
Log(current.Item1, current.Item2.id);
foreach (Foo child in current.Item2.Childs)
s.Push(new Tuple<int, Foo>(current.Item1 + 1, child));
}
}
Even though this is not the primary use case for dynamic objects (through you know all types involved at design time) you could also make use of System.Dynamic.ExpandoObject.
If you do so, be sure to test for performance differences because of the overhead.
public void DoSomeWorkWith(Foo x)
{
dynamic firstItem = new ExpandoObject();
firstItem.level = 1;
firstItem.item = x;
var s = new Stack<dynamic>();
s.Push(firstItem);
while (s.Any())
{
var current = s.Pop();
DoSomeBusiness(current.item);
DoSomeMoreBusiness(current.item);
Log(current.level, current.item.id);
foreach (Foo child in current.item.Childs)
{
dynamic next = new ExpandoObject();
next.level = current.level + 1;
next.item = child;
s.Push(next);
}
}
}
You could simplify your code by using recursion instead of pushing things onto a temporary stack and temporary objects. For example:
// (If you're not using C# 4, you can replace the default level with a function
// overload or just remove the default value)
void ProcessFooRecursive(Foo foo, int level = 0)
{
DoSomeBusiness(foo);
DoSomeMoreBusiness(foo);
Log(level, foo.id);
var newDepth = level + 1;
foreach (var child in foo.Childs)
{
ProcessFooRecursive(child, newDepth);
}
}