Traverse hierarchy object c# - c#

If I have a Class like below. How do i traverse through it until its property SomeObjects.count = 0
public class SomeObject
{
public String Name { get; set; }
public List<SomeObject> SomeObjects { get; set; }
}
Many Thanks

Here is a generic example of how you can traverse a composite object:
public static class TraversalHelper{
public static void TraverseAndExecute<T>(this T composite, Func<T,IEnumerable<T>> selectChildren, Action<T> action)
where T: class
{
action.Invoke(composite);
composite.TraverseAndExecute(selectChildren, action, new List<T>{ composite });
}
private static void TraverseAndExecute<T>(this T composite, Func<T,IEnumerable<T>> selectChildren, Action<T> action, IList<T> invokedComponents)
where T: class
{
invokedComponents = invokedComponents ?? new List<T>();
var components = selectChildren(composite) ?? new T[]{};
foreach(var component in components){
// To avoid an infinite loop in the case of circular references, ensure
// that you don't loop over an object that has already been traversed
if(!invokedComponents.Contains(component)){
action.Invoke(component);
invokedComponents.Add(component);
component.TraverseAndExecute<T>(selectChildren, action, invokedComponents);
}
else{
// the code to execute in the event of a circular reference
// would go here
}
}
}
}
Here is a sample usage:
public class Program{
public static void Main(){
var someObject = new SomeObject {
Name = "Composite",
SomeObjects = new List<SomeObject>{
new SomeObject{ Name = "Leaf 1" },
new SomeObject{
Name = "Nested Composite",
SomeObjects = new List<SomeObject>{ new SomeObject{Name = "Deep Leaf" }}
}
}
};
someObject.TraverseAndExecute(
x => x.SomeObjects,
x => { Console.WriteLine("Name: " + x.Name); }
);
}
}

This is a tree-like structure; there are many algorithms for traversing it, you can search for tree-traversal algorithms and you'll find many of them.

Related

Is there a way to write a generic method that fills lists of different types?

I have a parent class called Snack with subclasses Drink and Sweets. I want to store my Snacks in a "VendingMachine" Class where there is a list for each of the Products. However, I don't want to write the same method for each type of Snack. How would you write this as a generic method ?
// DRINKS LIST
List<Drink> drinks = new List<Drink>();
public List<Drink> Drinks { get => drinks; set => drinks = value; }
private void FillWithProducts <Product> (params Product[] products) where Product : Snack
{
Type typeParameter = typeof(Product);
Type drink = typeof(Drink);
foreach (Product p in products)
{
if (typeParameter.Equals(drink))
{
Drinks.Add(p);
}
}
}
If you really need to store each kinds of products in theair own list, you can use a dynamically populated dictionary where the key is the type, something like this.
private readonly Dictionary<Type, List<Product>> storeByType = new();
public List<Drink> Drinks => (List<Drink>)this.storeByType[typeof(Drink)]
private void FillWithProducts<Product>(params Product[] products) where Product : Snack
{
foreach (Product p in products)
{
var key = p.GetType();
if (!this.storeByType.ContainsKey(key)) {
// ... add new List<T> instantiated by reflection
// use MakeGenericType + Activator.CreateInstance for example
}
// cast to the non-generic interface
var list = (IList)this.storeByType[key];
list.Add(p);
}
}
Note, that the code is just present as an example to demonstrate the idea, missing many checks and safety, and might not even work as is.
I would keep a dictionary inside the VendingMachine that holds the snacks of different types with the type as the key. By doing so you avoid having to search a list with mixed types every time you want to fetch the items.
static void Main(string[] args)
{
var m = new VendingMachine();
m.AddRange(new Drink(), new Drink());
m.AddRange(new Sweet());
var drinks = m.Fetch<Drink>();
var sweets = m.Fetch<Sweet>();
}
public class VendingMachine
{
private readonly Dictionary<Type, List<Snack>> _snacks = new();
public void AddRange<T>(params T[] snacks) where T : Snack
{
var type = typeof(T);
if (_snacks.TryGetValue(type, out var existingSnacks))
existingSnacks.AddRange(snacks);
else
_snacks.Add(type, new List<Snack>(snacks));
}
public List<T> Fetch<T>() where T : Snack
{
if (_snacks.TryGetValue(typeof(T), out var existingSnacks))
return new List<T>(existingSnacks.Cast<T>());
return new List<T>();
}
}
I think maybe there's a different way of doing this. With your base SnackBase base class and derived Drink and Sweet classes, you can fill a VendingMachine class with snacks then get the drink and sweet lists from the vending machine. The code below illustrates this:
Base Class
internal class SnackBase
{
public string Name { get; }
protected SnackBase(string name)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentOutOfRangeException(nameof(name));
Name = name;
}
}
Derived classes
internal class Drink : SnackBase
{
public Drink(string name) : base(name) {}
}
internal class Sweet : SnackBase
{
public Sweet(string name) : base(name) {}
}
VendingMachine.cs
internal class VendingMachine
{
private readonly List<SnackBase> _snacks;
public VendingMachine(List<SnackBase> snacks)
{
_snacks = snacks;
}
public List<SnackBase> GetDrinks()
{
return _snacks.Where(s => s.GetType().Name == nameof(Drink)).ToList();
}
public List<SnackBase> GetSweets()
{
return _snacks.Where(s => s.GetType().Name == nameof(Sweet)).ToList();
}
}
Program.cs
internal static class Program
{
public static void Main()
{
var snacks = new List<SnackBase>
{
new Drink("Coke"),
new Sweet("Snickers"),
new Drink("Pepsi"),
new Sweet("Mars Bar"),
new Drink("7 Up"),
new Sweet("Reece's Pieces")
};
var vendingMachine = new VendingMachine(snacks);
Console.WriteLine("Drinks");
Console.WriteLine("------");
var drinks = vendingMachine.GetDrinks();
foreach (var drink in drinks)
{
Console.WriteLine(drink.Name);
}
Console.WriteLine("Sweets");
Console.WriteLine("------");
var sweets = vendingMachine.GetSweets();
foreach (var sweet in sweets)
{
Console.WriteLine(sweet.Name);
}
}
}
The vending machine class only needs one list of the common type (Snack)
Snacks
public abstract class Snack
{
protected Snack(string name)
{
Name = name;
}
public string Name { get; }
public abstract override string ToString();
}
public class Sweet : Snack
{
public Sweet(string name) : base(name)
{
}
public override string ToString() => $"Sweet({Name})";
}
public class Drink : Snack
{
public Drink(string name) : base(name)
{
}
public override string ToString() => $"Drink({Name})";
}
Vending Machine
public class VendingMachine
{
readonly List<Snack> _snacks;
public VendingMachine(params Snack[] snacks) => _snacks = new List<Snack>(snacks);
public VendingMachine(IEnumerable<Snack> snacks) => _snacks = new List<Snack>(snacks);
public IReadOnlyList<Snack> Snacks { get => _snacks; }
public IReadOnlyList<Drink> Drinks { get => _snacks.OfType<Drink>().ToList(); }
public IReadOnlyList<Sweet> Sweets { get => _snacks.OfType<Sweet>().ToList(); }
public void AddDrink(string name) => _snacks.Add(new Drink(name));
public void AddSweet(string name) => _snacks.Add(new Sweet(name));
}
Test Program
static class Program
{
static void Main(string[] args)
{
var vend = new VendingMachine();
vend.AddDrink("Joke Cola");
vend.AddSweet("Mersa Bar");
vend.AddDrink("Diet Goo");
vend.AddDrink("Bronto Care");
vend.AddSweet("Broken Tooth");
Console.WriteLine("Vending Machine Sweets");
foreach (var item in vend.Sweets)
{
Console.WriteLine(item);
}
Console.WriteLine();
Console.WriteLine("Vending Machine Drinks");
foreach (var item in vend.Drinks)
{
Console.WriteLine(item);
}
}
}
Sample Output
Vending Machine Sweets
Sweet(Mersa Bar)
Sweet(Broken Tooth)
Vending Machine Drinks
Drink(Joke Cola)
Drink(Diet Goo)
Drink(Bronto Care)

List of distinct types without casting to common superclass

How do I store objects in a list, but have them retain their original type? Without being casted to their common superclass.
So that the below code can work:
using System;
using System.Collections.Generic;
public class Test
{
public static void Main(string[] args)
{
var list = new List<Super>()
{
new Type1 { Number = 1, Info = "infomatin" },
new Type2 { Number = 2, Prop = "propty" }
};
foreach (var t in list)
{
Doer.Do(t);
}
}
}
public class Super
{
public int Number { get; set; }
}
public class Type1 : Super
{
public string Info { get; set; }
}
public class Type2 : Super
{
public string Prop { get; set; }
}
public static class Doer
{
public static void Do(Type1 arg)
{
Console.WriteLine($"Got type 1 with {arg.Info}");
}
public static void Do(Type2 arg)
{
Console.WriteLine($"Got type 2 with {arg.Prop}");
}
}
Wanted output:
Got type 1 with infomatin
Got type 2 with propty
Actual output, compiler error:
Test.cs(15,21): error CS1503: Argument 1: cannot convert from 'Super' to 'Type1'
I could do this inside the foreach
if (t instanceof Type1)
Doer.Do((Type1) t);
else if (t instanceof Type2)
Doer.Do((Type2) t);
But I don't want to have to write so much code. Especially as I add more subclasses of Super.
I would like to have to add only an additional method to my Doer to handle the new type, then the rest is taken care of.
You can use pattern matching, with only one Do() method
public static void Do(Super arg)
{
switch(arg)
{
case Type1 t1:
Console.WriteLine($"Got type 1 with {t1.Info}");
break;
case Type2 t2:
Console.WriteLine($"Got type 2 with {t2.Prop}");
break;
default:
throw new NotSupportedException();
}
}
As far as avoiding casting, I feel you have misunderstood how C# works. When you store in a base class, the memory contents are still the of the derived class and there is not casting in the sense of copying data from one type to another
This type of operation does not involve any data copy
Super obj = new Type1();
Neither does this
Type1 t1 = (Type1)obj;
It just the reference t1 putting on a different "mask" than obj, and the data behind the mask is the same.
Edit 1
A any casting (of the form (type)value in C#) is cast that might include a conversion or not. Some examples where obj is if type object, sup is of type class Super, t1 is of type class Type1 : Super and t2 is of type class Type2 : Super.
No conversion, or identity casts
obj = sup;
sup = t1;
sup = t2;
sup = t1; t1 = (Type1)sup;
obj = t1; sup = (Super)obj;
obj = t1; sup = (Type1)t1;
The code below requires the following custom conversion code to be added to Type1 and Type2 respectively.
public static implicit operator Type1(Type2 t2) => new Type1() { Info = t2.Prop };
public static explicit operator Type2(Type1 t1) => t1.Info.StartsWith("prop") ? new Type2() { Prop = t1.Info } : throw new NotSupportedExpection();
Implicit Conversion casts (data copy, may fail)
{ // Implicit conversion Type2 -> Type1
object obj = new Type2() { ID = 2, Prop = "propval" };
Type1 t1 = (Type2)obj;
}
Explicit Conversion casts (data copy, may fail)
{
// Explicit conversion Type1 -> Type2
object obj = new Type1() { ID = 1, Info = "propInfo" };
Type2 t2 = (Type2)(Type1)obj;
}
Read https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/conversions. for more accurate information.
If you find yourself using switch/case and casting, there's a good chance you're doing it wrong. With a properly designed object model, it shouldn't be necessary.
For example
abstract public class Super
{
public int Number { get; set; }
public abstract void Do();
}
public class Type1 : Super
{
public string Info { get; set; }
public override void Do()
{
Console.WriteLine($"Got type 1 with {this.Info}");
}
}
public class Type2 : Super
{
public string Prop { get; set; }
public override void Do()
{
Console.WriteLine($"Got type 2 with {this.Prop}");
}
}
Now you can just do this in your loop:
public static void Main(string[] args)
{
var list = new List<Super>()
{
new Type1 { Number = 1, Info = "infomatin" },
new Type2 { Number = 2, Prop = "propty" }
};
foreach (var t in list)
{
t.Do();
}
}
The above is consistent with Tell, Don't Ask which is a traditional object-oriented philosophy.
If you are worried about separation of concerns (for example, you don't want your classes to have knowledge of "Console") then you can inject the external functionality:
abstract public class Super
{
public int Number { get; set; }
public abstract void Do(Action<int> action);
}
public class Type1 : Super
{
public string Info { get; set; }
public override void Do(Action<int> action)
{
action(this.Info);
}
}
public class Type2 : Super
{
public string Prop { get; set; }
public override void Do(Action<int> action)
{
action(this.Prop);
}
}
public static void Main(string[] args)
{
var list = new List<Super>()
{
new Type1 { Number = 1, Info = "infomatin" },
new Type2 { Number = 2, Prop = "propty" }
};
foreach (var t in list)
{
t.Do( x => Console.WriteLine("The value that we're interested in is {0}", x));
}
}
There is one more situation which may apply here (based on your comments). Let's say you have "clean" DTO objects that have no methods, and you don't want to add any for whatever reason, e.g. maybe the DTOs are code-generated and you can't modify them. This is actually a common situation (I like methodless DTOs too).
To make the situation more real, let's use more meaningful examples. Let's say you have a variety of objects that might contain an end user's name, but with various different identifiers:
abstract public class Super
{
}
public class Type1 : Super
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Type2 : Super
{
public string FullName { get; set; }
}
The temptation here is to write a switch case like this:
foreach (var t in list)
{
switch (t)
{
case Type1 type1 : Console.WriteLine("Name is {0} {1}", type1.FirstName, type1.LastName);
case Type2 type2: Console.WriteLine("Name is {0}", type2.FullName);
default:
throw new InvalidOperationException();
}
}
The problem here is that runtime throw, which will occur any time someone adds a different object type but doesn't remember to update your switch statement. This might not be a problem, but it also might be a huge problem, e.g. if your DTOs are held in a separate library from your Do processor and you would rather not have to update both at the same time (which could be a deployment concern under certain architectures).
What is missing here is a business concept of "Name" agnostic of where it came from. Somewhere, some code has to translate these various objects into something that has a name, and preferably that logic would be encapsulated somewhere.
That's where I would use an adapter class.
class NameHolder
{
public string FullName { get; }
public NameHolder(Type1 type1)
{
this.FullName = type1.FirstName + " " + type1.LastName;
}
public NameHolder(Type2 type2)
{
this.FullName = type2.FullName;
}
}
With the addition of this missing business concept, the logic become very simple:
public static void Main(string[] args)
{
var list = new List<NameHolder>()
{
new NameHolder(new Type1 { Number = 1, Info = "infomatin" }),
new NameHolder(new Type2 { Number = 2, Prop = "propty" })
};
foreach (var t in list)
{
Do(t.FullName);
}
}
Notice the lack of throw. The advantage of this approach is that all types are resolved at compile time, so if you forget to add logic to map the proper fields, you will get a compile time error, which you can detect and fix immediately.

Affect an ICollection that is passed to a method as a parameter?

I have a class that has an ICollection property that is assigned by the constructor when the class is instantiated, but I want to bind this property to the original collection so that when it's updated/changed, the original list is as well. What is the best method of doing this?
Here's an example:
public class Organizations
{
private ICollection<Organization> _orgs;
public Organizations(ICollection<Organization> orgs)
{
_orgs = orgs;
}
public void TestAdd()
{
_orgs.Add(new Organization {Name = "Testing 123"});
}
}
// in another class
public ActionResult TestApi()
{
var tmp = new SyncTool.Core.Extensions.Zendesk.Organizations(ZendeskCache.Organizations.Data);
var zd = ZendeskCache.Organizations.Data.FirstOrDefault(n => n.Name.Contains("Testing 123"));
//ZendeskCache.Org.... is a List<Organization>
return Json(new {data = "tmp" }, AG);
}
The List<Organization> you are passing to the constructor is a reference object. This code works the way you want it to (aside from syntax errors), have you tried it out?
To reproduce more simply:
public class Program
{
public static void Main(string[] args)
{
var orgs = new List<string>();
var orgClass = new Organizations(orgs);
orgClass.TestAdd();
Console.WriteLine(orgs.First());
Console.Read();
}
}
public class Organizations
{
private ICollection<string> _orgs;
public Organizations(ICollection<string> orgs)
{
_orgs = orgs;
}
public void TestAdd()
{
_orgs.Add("Testing 123");
}
}
//Output: "Testing 123"

Is it possible to call a method on all generic properties in a class?

I have a class with a lot of properties of type IDbSet<SomeClass>:
public InMemoryContext : IContext
{
public IDbSet<ClassA> ClassASet { get; set; }
public IDbSet<ClassB> ClassBSet { get; set; }
[...]
public void SaveChanges()
{
//TODO: this is relevant part
}
}
All these properties are instantiated in the constructor:
public InMemoryContext
{
ClassASet = new InMemoryDbSet<ClassA>();
[...]
}
InMemoryDbSet has one relevant method for this question:
public class InMemoryDbSet<T> : IDbSet<T> where T : class
{
public void SaveChanges()
{
[...]
}
}
I'd like to get all properties that are IDbSet and call SaveChanges() on them in a loop, so I don't need to repeat all set names second time.
I tried with reflection but as my properties are generic I can't get it to work. I tried making ClassA and ClassB deriving from same common interface but still no luck.
Is it actually possible to do it without explicitly specifying all sets? What should I change to achieve this result?
I imagine the pseudocode to be like:
public void SaveChanges()
{
foreach(var set in GetDbSetsFromClass(this))
{
set.SaveChanges();
}
}
With reflection I tried:
public void SaveChanges()
{
SaveChangesCalled = true;
var properties = GetType().GetProperties().Where(p => p.PropertyType.IsInterface && typeof(IDbSet<IEntity>).IsAssignableFrom(p.PropertyType)).Cast<InMemoryDbSet<IEntity>>();
foreach (var property in properties)
{
CallSaveChanges(property);
}
}
And the list is empty
public bool SaveChangesCalled { get; set; }
private static void CallSaveChanges<T>(InMemoryDbSet<T> set) where T : class
{
set.SaveChanges();
}
EDIT: The public properties must remain visible at the top level of InMemoryContext class as this is restricted by the interface defining EF context.
Your code doesn't work because IDbSet<Class> is not assignable to IDbSet<IEntity>, because IDbSet<T> is not covariant.
What you want is to find properties whose type's generic definition is IDbSet<>.
var properties = typeof (InMemoryContext)
.GetProperties()
.Where(p => p.PropertyType.IsGenericType &&
p.PropertyType.GetGenericTypeDefinition() == typeof (IDbSet<>));
To invoke your CallSaveChanges method, you'll need this:
foreach(var proparty in properties)
{
var value = property.GetValue(this);
var entityType = property.PropertyType.GetGenericArguments().First();
var callSaveChanges = this.GetType()
.GetMethod("CallSaveChanges", BindingFlags.NonPublic | BindingFlags.Static);
var constructedCallSaveChanges = callSaveChanges.MakeGenericMethod(entityType );
constructedCallSaveChanges.Invoke(null, BindingFlags.NonPublic | BindingFlags.Static, null, new object[]{ value }, CultureInfo.InvariantCulture);
}
That being said, I don't think this scenario warrants the usage of reflection.
For a simple solution you could try something like this
public InMemoryContext
{
public Dictionary<string, object> allSets = new Dictionary<string, object>();
public InMemoryContext()
{
allSets.Add("classA", new InMemoryDbSet<ClassA>());
allSets.Add("classB", new InMemoryDbSet<ClassB>());
[...]
}
public IDbSet<ClassA> ClassASet { get
{
return allSets["classA"];
}
set
{
allSets["classA"] = Value
}
}
[...]
public void SaveChanges()
{
//TODO: this is relevant part
foreach(var kvp in allSets)
{
kvp.Value.SaveChanges();
}
}
}
Some things maybe wrong since it's just off top of my head.. but you should get the idea

I want to pass a parameter into the constructor of a list of certain classes

I'd like to somehow pass a parameter into a list when I'm instantiating a new list of certain classes.
More specifically, I'd like to do something like the following:
List<FirstClass>(dataTable);
List<SecondClass>(dataTable);
If the first line of code is called, the constructor will deal with dataTable in a certain way than if the latter is called (FirstClass has different fields).
What I've Tried
namespace DeeMacsNamespace
{
public class FirstClass
{
public String Title { get; set; }
public String Url { get; set; }
}
public class FirstClass : List<FirstClass>
{
public FirstClass(DataTable table)
{
foreach (DataRow row in table.Rows)
{
this.Add(new FirstClass()
{
Title = (String)row["Title"]
});
}
}
}
}
I'm assuming (or at least hoping), the above will work. But how do I then most efficiently reuse this code that reads from a DataTable in a really similar constructor for another list of a certain class? And how do I incorporate a conditional statement to check whether the constructor is from the FirstClass or SecondClass type? I would like to avoid rewriting this for a similar constructor for SecondClass.
If I've understood you correctly, then use something like this:
class MyCollection<T> : Collection<T>
{
public MyCollection(DataTable dataTable, Func<DataRow, T> itemsFactory)
: base(dataTable.Rows.Cast<DataRow>().Select(row => itemsFactory(row)).ToList())
{
}
}
var firstClassCollection = new MyCollection<FirstClass>(dataTable, row => new FirstClass
{
Title = (String)row["Title"],
Url = (String)row["Url"]
});
class FirstClass <T>
{
if (typeof(T) == typeof(FirstClass))
{
// ... snip
}
}
Then have all other classes inherit from FirstClass.
There's some unanswered questions regarding your intent. That being said this generic setup may fit the bill:
public interface ISomeInterface
{
String Title { get; set; }
String Url { get; set; }
}
public class SecondClass : ISomeInterface
{
public String Title { get; set; }
public String Url { get; set; }
}
public class FirstClass : ISomeInterface
{
public String Title { get; set; }
public String Url { get; set; }
}
public class SomeClassCollection<T> : List<T> where T: ISomeInterface, new()
{
public SomeClassCollection(DataTable table)
{
foreach (DataRow row in table.Rows)
{
this.Add(new T()
{
Title = (String)row["Title"]
});
}
}
}
private static void Main()
{
var table = new DataTable();
var collection = new SomeClassCollection<FirstClass>(table);
}
You could write an extension method such as:
public static class DataTableEx
{
public static IList<T> CreateList<T>(this DataTable dt,
Func<DataRow,T> selector)
{
return dt.Rows.Cast<DataRow>().Select(selector).ToList();
}
}
Then use it as follows:
IList<string> myList =
dataTable.CreateList(r => new FirstClass{Title = (string)r["Title"]});
List<FirstClass> MyList1 = dataTable.Rows.Select(Row =>
new FirstClass()
{
Title = (String)Row["Title"]
}
).ToList();
List<SecondClass> MyList2 = dataTable.Rows.Select(Row =>
new SecondClass() { the way you create second class } ).ToList();
First of all, you don't want to have the two classes have the same name (the FirstClass and the ListOfFirstClass).
Second of all, your question is a bit unclear, but I believe you're trying to turn a DataTable into a list of First/SecondClass.
If you have access to the DataTable class, you can have it implement the IEnumerable interface.
Using Linq, you can do comething like:
using System.Linq;
public class DataTable : IEnumerable<T>
{
IEnumerable<T> IEnumerable<T>.GetEnumerator()
{
return from row in rows
where FindIfRowIsOfClass<T>(row)
select new T(row);
}
}
You'll have to implement the generic IEnumerable method as well, and fill out the FindIfRowIsOfClassT method. This will most likely be done by finding out if it has the right fields.
The result is the ability to do
List<FirstClass> listOfFirstClass = new List<FirstClass>(dataTable.GetEnumerator<FirstClass>);
I'm not 100% sure, but you may actually be able to get away with not calling GetEnumerator explicitly, List might do that for you.
If you don't have access to that, you can do it manually:
var enumerableFirstClass = from row in dataTable.rows
where <insert row is FirstClass check>
select new FirstClass(){Title = (string)row["Title"]};
List<FirstClass> listOfFirstClass = new List<FirstClass>(enumerableFirstClass);
Hope this helps.
Happy coding!

Categories

Resources