I need help making this method generic. It is repeated about ten times to get lists for different web list controls (substituting "MyType" for the type used in the particular control).
private static IList<MyType> GetList(RequestForm form)
{
// get base list
IMyTypeRepository myTypeRepository = new MyTypeRepository(new HybridSessionBuilder());
IList<MyType> myTypes = myTypeRepository.GetAll();
// create results list
IList<MyType> result = new List<MyType>();
// iterate for active + used list items
foreach (MyType myType in myTypes)
{
if (myType.Active || form.SolutionType.Contains(myType.Value))
{
result.Add(myType);
}
}
// return sorted results
result.OrderBy(o => o.DisplayOrder);
return result;
}
Let me know if this isn't enough information. I think this requires more advanced language features that I'm just getting acquainted with. Maybe I should make them all use the same repository?
Thanks for your help.
EDIT:
Thanks for your help. I don't have any peer support, so this board is fantastic and I learned something from each of you. I wish I could accept all the answers.
You could firstly make your function a bit more terse like this:
private static IList<MyType> GetList(RequestForm form)
{
// get base list
IMyTypeRepository myTypeRepository =
new MyTypeRepository(new HybridSessionBuilder());
IList<MyType> myTypes = myTypeRepository.GetAll();
return myTypes.Where(x => x.Active || form.SolutionType.Contains(x.Value))
.OrderBy(x => x.DisplayOrder).ToList();
}
At that point, most of the content of the function is directly related to MyType, so how you can further improve it depends largely on how MyType relates to the other types involved. For example, here is a hypothetical version that you could write if your other types followed a reasonable-looking (to me) contract:
private static IList<T> GetList(RequestForm form) where T : OrderedValueContainer
{
// we'll want to somehow genericize the idea of a TypeRepository that can
// produce these types; if that can't be done, we're probably better off
// passing a repository into this function rather than creating it here
var repository = new TypeRepository<T>(new HybridSessionBuilder());
IList<T> myTypes = repository.GetAll();
// the hypothetical OrderedValueContainer class/interface
// contains definitions for Active, Value, and DisplayOrder
return myTypes.Where(x => x.Active || form.SolutionType.Contains(x.Value))
.OrderBy(x => x.DisplayOrder).ToList();
}
If all the types implement the same interface, (if they don't then make them, and make sure to add all the properties to the interface that are needed in this method) then you can do something like this:
private static IList<T> GetList(RequestForm form)
where T: IMyInterface
{
// get base list
IMyTypeRepository myTypeRepository = new MyTypeRepository(new HybridSessionBuilder());
IList<T> myTypes = myTypeRepository.GetAll();
// create results list
IList<T> result = new List<T>();
// iterate for active + used list items
foreach (T myType in myTypes)
{
if (myType.Active || form.SolutionType.Contains(myType.Value))
{
result.Add(myType);
}
}
// return sorted results
return result.OrderBy(o => o.DisplayOrder).ToList();
}
One other change I made is the last line, where you had the orderby on a seperate line and were never actually capturing the Ordered list.
EDIT: To solve the repository problem, you can have a repository factory of sorts that returns the correct repository based on the type of T:
public static IMyTypeRepository GetRepository(Type t)
{
if(t == typeof(Type1))
{
return Type1Repository();
}
if(t == typeof(Type2))
{
return Type2Repository();
}
.......
}
Assuming of course that all your repositories implement the IMyRepository interface.
First of all, all your types must implement a common interface that define properties like Active, Value ...
Also, for what I can tell, there must be a repository interface for all repositories independently of the MyType so that you can use a generic method like this. The GetAll() method should be defined in the IRepository.
public interface IRepository<T> where T : IMyType
{
IList<T> GetAll();
}
public class RepositoryFactory
{
public static IRepository<T> createRepository<T>(ISessionBuilder sb) where T : IMyType
{
// create repository
}
}
public interface IMyType
{
bool Active { get; }
string Value { get; }
}
private static IList<T> GetList(RequestForm form) where T : IMyType
{
// get base list
IRepository<T> repository = RepositoryFactory.createRepository<T>(new HybridSessionBuilder());
IList<T> myTypes = repository.GetAll();
// create results list
IList<T> result = new List<T>();
// iterate for active + used list items
foreach (T myType in myTypes)
{
if (myType.Active || form.SolutionType.Contains(myType.Value))
{
result.Add(myType);
}
}
// return sorted results
return result.OrderBy(o => o.DisplayOrder).ToList();
}
Assuming that the repositories share a common interface, the issue with the repository should be easy to fix: add a static function such as
public static IRepository RepositoryForType(Type t)
{
if(t == typeof(SomeClass))
return new SomeClassRepository(new HybridSession());
else if ...
else throw new InvalidOperationException("No repository for type " + t.Name);
}
This should require you the least amount of changes to your existing code, but mind that in the future you'll have to add classes support for new repositories in this function as you add new repositories in your project (if you're using unit testing you'll easily figure out if you forgot about this helper anyway).
Related
I am handling classes that wrap collections. For example:
public class CollA
{
public List<SomeType> Items {get;set;}
// other properties I'm not interested in
}
I am guaranteed that the collection classes will have exactly ONE property that is of List<T>
Now, I find myself with a requirement such that I may have many instances of CollA and I am asked to return a new instance of CollA where the property Items contains a union of the Items properties of the individual CollA instances. So, for example:
var A = new CollA(Items = new List<SomeType>
{
new SomeType("A"), new SomeType("B")
};
var B = new CollA(Items = new List<SomeType>
{
new SomeType("C"), new SomeType("D")
};
var result = SomeMythicalCombine(A, B);
// result.Items == { new SomeType("A"), new SomeType("B"), new SomeType("C"), new SomeType("D") }
This, if the types are all known at compile time is easy, but I need to do it with the types not being known until runtime.
I've got part of the way, I think, using reflection....
public T SomeMythicalCombine (params object[] collections)
{
var collectionType = typeof(T);
var listProperty = collectionType.GetProperties()
.Single(p=> typeof(IList).IsAssignableFrom(p.PropertyType));
var listPropertyName = listProperty.Name;
var result = Activator.CreateInstance(collectionType);
var innerType = listProperty.PropertyType.GenericTypeArguments[0];
var listType = typeof(List<>).MakeGenericType(innerType);
var list = Activator.CreateInstance(listType);
foreach(var collection in collections)
{
var listValues = collection.GetType().GetProperty(listPropertyName).GetValue(collection);
// listItems is an object here and I need to find a way of casting it
// to something I can iterate over so I can call (list as IList).Add(something)
}
// Then, I think, all I need to do is set the appropriate property on the
// the result item
result.GetType().GetProperty(listPropertyName).SetValue(result, list);
return result as T;
}
Can anyone fill in the gap in my thinking, please?
So basically if you know the type at compile time, you can do this:
var result = new CollA { Items = new[] { A, B }.SelectMany(c => c.Items).ToList() };
If you can require all your collection wrappers to implement an interface, it should be pretty simple to extract this into a generic method.
public interface ICollectionWrapper<T> { List<T> Items { get; set; } }
T SomeMythicalCombine<T, T2>(params T[] wrappers) where T : ICollectionWrapper<T2>, new()
{
return new T() { Items = wrappers.SelectMany(w => w.Items).ToList() };
}
That presupposes you can call the method with the right generic parameter. If your calling code knows the types of the collections you're dealing with, you can do this:
var result = SomeMythicalCombine(A, B);
But honestly if your calling code knows that, you might be better off using the first code snippet: it's concise and clear enough. Assuming you literally have a collection of objects that you just happen to know will all have the same run-time type, you should be able to use a little reflection to get that type and invoke the helper method with the right generic parameters. It's not ideal, but it might be faster/simpler than writing the entire method to work using reflection.
you can do this : var combined = A.Items.Concat(B.Items).
However, if the property is a part of interface or base class implementation, then you can target the implementation instead something like this :
public IList<TResult> SomeMythicalCombine<TResult>(params IInterface[] collection) // use interface or base class
{
// assuming that all collection would have the same element type.
}
if it is not a part of other implementations, then you can implement an interface and apply it to all classes, this would be an insurance that this collection will always be there as long as the class implements the interface.
if it's hard to achieve that, then you can and you see that reflection is your best option, you can use something like this :
// assuming all collections have the same property of type List<TResult> type.
// if they're different, then return an object instead. and change List<TResult> to IList
public IEnumerable<TResult> CombineLists<T, TResult>(params T[] instances)
where T : class
{
if (instances?.Any() == false) yield break;
foreach(var obj in instances)
{
if (obj == null) continue;
var list = obj.GetType()
.GetProperties()
.FirstOrDefault(p => typeof(List<TResult>).IsAssignableFrom(p.PropertyType))
?.GetValue(obj) as List<TResult>;
if (list?.Count == 0) continue;
foreach (var item in list)
yield return item;
}
}
usage :
var combined = CombineLists<CollA, string>(A, B);
I have a class composed of multiple lists and I have generic methods to allow me to do CRUD (and other) operations over those lists.
I'm basically trying to do a variation of DbContext.Set<T> with List.
This is my situation:
public class A
{
private IList<B> Bs;
private IList<C> Cs;
public A()
{
Administrators = new List<B>();
Developers = new List<C>();
}
public void Add<T>(T entity)
{
var propertyIWant = this.GetType().GetProperties().Where(p => p.GetType() == typeof(IList<T>));
var propertyAsList = propertyIWant as List<T>;
propertyAsList.Add(entity);
}
public void Delete<T>(T entity)
{
//Same idea
}
//Other methods
}
The problem is that my code gives me a list of the desired type, but no the actual list (i.e. the property).
So any modifications to that list don't modify the property.
I'd like to be able to do something akin to A.List<T> to get the list of that type (like DbContext can do with DbContext.Set<T>).
You have made a few mistakes here.
Bs and Cs are fields, not properties, so you should use GetFields.
Bs and Cs are private, so you should use the binding flags NonPublic and Instance
The result you get from Where is an IEnumerable<T>. You should call FirstOrDefault or SingleOrDefault to get a single field info.
After getting the field info, you need to call GetValue to get the field's value.
p.GetType() returns typeof(FieldInfo), not the declared type of the field. You should use FieldType instead.
Here is the fixed version:
public void Add<T>(T entity)
{
var fieldIWant = this.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(p => p.FieldType == typeof(IList<T>)).SingleOrDefault();
if (fieldIWant != null) {
var value = (IList<T>)fieldIWant.GetValue(this);
value.Add(entity);
}
}
Or, as thehennyy said in the comments, you should probably look into expression trees, specifically MemberExpressions to do this. Reflection is quite slow.
I'm experimenting with extending classes and managed to extend List<T> for fun like so:
public static void SomeCustomSort<T>(this List<T> list, string item)
{
if (typeof(T) != typeof(string) || list.Count == 0)
return;
// doStuff();
}
I wondered if there was a smarter way to extend List<T> only for List<string> so that my extension method is not listed or accessable for any other type T
Just make your method non-generic:
public static void SomeCustomSort(this List<string> list, string item)
and specify exact type it should work with
NOTE: With void methods even if you want to restrict extension method parameter to some set of types (e.g. all implementors of some interface or some non-sealed class with classes derived from it) I would not recommend using generic method with parameter constraint:
public static void SomeCustomSort<T>(this List<T> animals)
where T: IAnimal
Why? Because it overcomplicates your code. Non-generic method is more simple to understand than generic method. Generic method without constraint is more simple to understand than generic method with constraint. You should start from the simplest solution which is easy to understand. What sounds more natural to you?
"It sorts list of animals"
"It sorts list of items of any type"
"It sorts list of items of any type which is animal"
When to use generic type constraint? When you return items from your method and you don't want to lose information about the exact type of list items. Consider method which returns animals by some weight filter
public static IEnumerable<IAnimal> WhereWeightBelow(this List<IAnimal> animals, int weight)
If you'll pass list of dogs to this method, you will lose intellisense for all dog-specific information in the method output.
dogs.WhereWeightBelow(10).Where(d => d. /* oops only IAnimal members here */)
Returning generic type will preserve all dog info for you.
Another alternative not yet mentioned:
public static void SomeCustomSort<T>(this List<T> list, string item)
where T: YourSpecificType
This allows you to specify more than just one type, for example:
public static void SomeCustomSort<T>(this List<T> list, string item)
where T: ISortable, ICustomInterface
Just specify T instead of making it a generic method.
public static void SomeCustomSort(this List<string> list, string item)
Just define exactly string type on your extension method
public static void SomeCustomSort(this List<string> list, string item)
{
// doStuff();
}
You can also use a constraint like this (in this example T would have to be of type Project):
public static void SomeCustomSort<T>(this List<T> list, string item)
where T : Project
{
}
I would show you in the following example how you can easily expand a generic list.
I expanded the list to return random data from the list itself.
We have a class for example:
public class ExampleClass
{
public string Name { get; set; }
}
We have now made a list of these classes in some method:
var exampleList = new List<ExampleClass>()
{
new ExampleClass()
{
Name = "Class1"
},
new ExampleClass()
{
Name = "Class2"
},
new ExampleClass()
{
Name = "Class3"
}
};
var randomList = exampleList.Random(2);
The following is a simple implementation of returning random objects from a list
public static class ListExtensions
{
public static IList<T> Random<T>(this IList<T> list, int numberOfResult) where T : class
{
if (list == null) throw new ArgumentNullException(nameof(list));
if (numberOfResult <= 0 || numberOfResult > list.Count) throw new ArgumentOutOfRangeException(nameof(numberOfResult));
var random = new Random();
var randomList = new List<T>();
var randomNumbers = new List<int>();
while (randomList.Count < numberOfResult)
{
var index = random.Next(list.Count);
if (randomNumbers.IndexOf(index) < 0)
{
randomNumbers.Add(index);
randomList.Add(list[index]);
}
}
return randomList;
}
}
I 'm trying to build a DI container and I 've stumbled on to the following problem: I have a method that retrieves a list of registered instances for a given type and I want to use that to inject IEnumerable<T> properties in a given object. An example of what I am trying to achieve would be the following:
class A { public IList<IExample> Objects { get; set; } }
class B: IExample {}
class C: IExample {}
Container.Register<IExample>(new B());
Container.Register<IExample>(new C());
var obj = new A();
Container.Inject(A);
Debug.Assert(A.Objects != null && A.Objects.Count == 2);
My Retrieve method returns an IList<object>, mainly because I have no type information at that moment, so I am attempting to convert that list into a List<T> at injection time. Here is a succint form of the methods doing the work:
public virtual IList<object> Retrieve(Type type)
{
var instances = Registry[type];
foreach(var instance in instances)
Inject(type, instance); // omitted
return instances;
}
public virtual void Inject<T>(T instance)
{
var properties = typeof (T).GetProperties();
foreach (var propertyInfo in properties)
{
var propertyType = propertyInfo.PropertyType;
if (!IsIEnumerable(propertyType)) continue;
var genericType = propertyType.GetGenericArguments()[0];
propertyInfo.SetValue(instance,
GetListType(genericType, Retrieve(genericType)), null);
}
}
protected virtual object GetListType(Type type, IEnumerable<object> items)
{
return items.Select(item => Convert.ChangeType(item, type)).ToList();
}
The code returns the error: System.InvalidCastException : Object must implement IConvertible. Sadly, I don't know how to proceed from here. Perhaps I am doing this all wrong. I 've thought of using generics or injecting multiple properties by hand, but I'd really like to not have to do that.
Thanks in advance for any help or ideas.
You could create a generic list like this:
public virtual IList Retrieve(Type type)
{
// ...
listType = typeof(List<>).MakeGenericType(new Type[] { type });
IList list = (IList)Activator.CreateInstance(listType);
// ...
return list
}
this list can be casted to IList<T>, because it is one.
You could consider to use IEnumerable and Cast<T>, but then you don't have an instance of a list. I don'^t know how important it is to have one.
I have a user defined class that I want to create a public List as part of. I want the List to be a List of delegate functions that I can add to and set each List Member to a delegate function. I want this list of functions to be part of the class I instantiate, so it follows the instance of the class as I pass it to other functions. I need the ability to call the delegated functions via a foreach loop, so it also has to be IEnumberable.
I've been trying for several hours, what I have may or may not do part of the job. When it started looking like I needed to write my own IEnumberation routines for the custom List, I realize I was in way over my head and came here.
This is the code I have:
public delegate List<ChartTestModel> MyDelegate<T>(T i);
public class DelegateList<T>
{
public void Add(MyDelegate<T> del)
{
imp.Add(del);
}
public void CallDelegates(T k)
{
foreach (MyDelegate<T> del in imp)
{
del(k);
}
}
private List<MyDelegate<T>> imp = new List<MyDelegate<T>>();
}
I don't even know if this does what I want it to or not. I know I can't ForEach through it, though. It's written entirely from pieced together code from looking on Google. I barely understand what it's supposed to do.
I don't see why you need a custom class at all. Just use List<T> where T is whatever delegate type.
List<Action> actions = new List<Action>();
actions.Add(() => blah blah);
actions.Add(Whatever); // Whatever() is a method
// now run them all
actions.ForEach(a => a());
IEnumerable<T> is simple to implement, particularly when you have a collection as a member of the class. All you need to do is define appropriate GetEnumerator methods, and the easiest thing to do is return the enumerator of the underlying collection.
class YourClass : IEnumerable<SomeClass>
{
List<SomeClass> list = ...
public IEnumerator<SomeClass> GetEnumerator()
{
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Here, you implement methods for implicitly for IEnumerable<T> and explicitly for IEnumerable. (You have to implement both as IEnumerable<T> inherits IEnumerable.)
For your specific class, you might have
public class DelegateList<T> : IEnumerable<MyDelegate<T>>
{
// ...other class details
private List<MyDelegate<T>> imp = new List<MyDelegate<T>>();
public IEnumerator<MyDelegate<T>> GetEnumerator()
{
return imp.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
I hope this is userful to you.
static void Main(string[] args)
{
var delegateFuncs = new List<Func<string , string>> {
l1=>{ return "at1:" + l1;} ,
l2=>{ return "at2:" + l2;} ,
l3=>{ return "at3:" + l3;} ,
l4=>{ return "at4:" + l4;}
};
string parameter = "test";
foreach (var f in delegateFuncs)
{
Console.WriteLine(f(parameter));
}
Console.ReadLine();
}