Generic list/sublist handling - c#

Let's say we have a class
class ComplexCls
{
public int Fld1;
public string Fld2;
//could be more fields
}
class Cls
{
public int SomeField;
}
and then some code
class ComplexClsList: List<ComplexCls>;
ComplexClsList myComplexList;
// fill myComplexList
// same for Cls
class ClsList : List<Cls>;
ClsList myClsList;
We want to populate myClsList from myComplexList, something like (pseudocode):
foreach Complexitem in myComplexList
{
Cls ClsItem = new Cls();
ClsItem.SomeField = ComplexItem.Fld1;
}
The code to do this is easy and will be put in some method in myClsList.
However I'd like to design this as generic as possible, for generic ComplexCls.
Note that the exact ComplexCls is known at the moment of using this code, only the algorithm shd be generic.
I know it can be done using (direct) reflection but is there other solution?
Let me know if the question is not clear enough. (probably isn't).
[EDIT] Basically, what I need is this: having myClsList, I need to specify a DataSource (ComplexClsList) and a field from that DataSource (Fld1) that will be used to populate my SomeField

This is just a mapping, so use some simple LINQ:
ClsList myClsList = new ClsList();
myClsList.AddRange(
myComplexList.Select(Complexitem => new Cls { SomeField = Complexitem.Fld1 })
);

Okay, the easier version assuming we have a known target field on a class (I've written this as an extension method, no need to do
public IEnumerable<Cls> MapField<TSource>(IEnumerable<TSource> sourceList,
Func<TSource, int> sourceSelector)
{
return sourceList.Select(x => new Cls {SomeField = sourceSelector(x)});
}
Called this way
IEnumerable<Cls> result = MapField(myComplexList, x => x.Fld1);
Aside: Since your myComplexList of type ComplexClsList inherits from List (which implements IEnumerable this will work. The result isn't of type ClsList that you wanted, but you could easily call .ToList() on the result and provide a constructor on ClsList that takes a List<Cls>.
And the more complicated version for when we don't know the target field (or type)...
public IEnumerable<TResult> MapField<TSource, TResult, TMap>(
IEnumerable<TSource> sourceList,
Func<TSource, TMap> sourceSelector,
Func<TMap, TResult> resultCreator)
{
return sourceList.Select(x => resultCreator(sourceSelector(x)));
}
Not as pretty to call....
IEnumerable<Cls> result = MapField(
myComplexList,
source => source.Fld1,
valueToMap => new Cls() {SomeField = valueToMap});
Might be a better way, but it's not occurring to me at the moment.
Edit: Actually, you could combine the two Func on the last one into a single one that takes a TSource and creates and maps the necessary fields to TResult, but I'm really not sure what you're gaining with that extra layer of abstraction...

You may want to reconsider extending List classes in the first place. What does inheritance give you, in this case? I suspect that you'll be better off favoring composition over inheritance here. One possible approach would be:
// If you would say that a ComplexCls "is a" Cls, then maybe your inheritance
// relationship belongs here instead.
public class ComplexCls : Cls {
}
public class ClsList
{
public IReadOnlyCollection<Cls> Items {get;set;}
}
public class ComplexClsList
{
public IReadOnlyCollection<ComplexCls> Items {get;set;}
}
Then you can create a ClsClist easily.
ClsList basicList = new ClsList{Items = complexList.Items};
But you may want to take it a step farther and question why the ClsList and ComplexClsList classes exist at all. Why not simply pass around Lists directly. I mean, what's the difference between a ClsList and a "List of Clses" (List<Cls>)?

Related

Not getting properties on generic parameter T when trying to create generic comparer to compare 2 objects

I want to compare 2 objects like below :
I don't want 2 employees in the same department
I don't want 2 Animals in the same Zoo
So on.....
I am trying to implement IEqualityComparer to accept generic type argument to take Employee or Animals or any object and to the comparison for me but I ran into a problem because since I have T as an argument, I am not getting the properties on T.
public class GenericComparer<T> : IEqualityComparer<T>
{
public bool Equals(T x, T y)
{
// Not getting a property here T.??
//now if I would have T as an Employee then I could do something like this here:
// return String.Equals(x.Name, y.Name); // but this will work because I know that Name is string
// but lets say if I have a different datatype that I want to compare then how can i do that?
}
public int GetHashCode(T obj)
{
return obj.
}
}
The thing is I don't want to create 2 classes like EmployeeComparer: IEqualityComparer<Employee> and AnimalComparer : IEqualityComparer<Animal> as the code will be somewhat similar.
Is it possible to create generic compared to compare any objects of the same types?
Update:
I am just trying to understand the limitations of generics with reference types. When should we create a Generic class or methods to accept reference types and when we should not.
I was thinking that since List<T> can accept anything like List<Employee> or List<Animal> or anything then why my GenericComparer cannot.
Because when we create List<Employee> then we can run for each loop and access the properties right:
foreach(var employee in employees)
{
string name = employee.Name; //Here we are able to access the properties
}
Then how come not in my case?
Answer to all the above questions will help me understand generics better with reference type especially. So, if someone can provide an answer to all this question and why it is not possible with my GenericComaprer<T> and how it is possible with List<T> then I will really appreciate :)
when we create List<Employee> then we can run for each loop and access the properties
Sure, because you are using List<T> by providing it the concrete type (Employee) to use in place for T. But if you were to look at the internals of (or try to write your own version of) List<T> you'd see you have no way to access an hypothetical Name property of T. That is because C# is a strictly typed language (contrarily to Javascript or Python for example). It won't let you use anything it doesn't know beforehand and for sure exists.
The way you defined your GenericComparer<T> class, all the compiler know is that T can do what every C# objects can, that is... not much (you can use ToString() and GetType() and compare references, that's pretty much all).
Then, because otherwise generics wouldn't be very useful, you can specify constraints on your generic type: you can, for example, tell the compiler that T must implement some interface. If you do this, then you can access any member defined in this interface on your T instances from inside the generic class.
For the sake of the example, let's say you have employees and animals; both employees and animals have a Name, then employees also have a Salary but animals have a Species. This could look like this:
public class Employee {
public string Name { get; set; }
public double Salary { get; set; }
}
public class Animal {
public string Name { get; set; }
public string Species { get; set; }
}
If we keep it that way, as explained above, you won't be able to access the properties from your generic class. So let's introduce an interface:
public interface IHasName {
string Name { get; }
}
And add : IHasName at the end of the Employee and Animal class declarations.
We also need to tweak GenericComparer declaration this way:
public class GenericComparer<T> : IEqualityComparer<T> where T : IHasName
By doing this, we tell the compiler that the T in GenericComparer must implement IHasName. The benefit is now you can access the Name property from within GenericComparer. On the other side, you won't be able to use GenericComparer by passing it anything that doesn't implement IHasName. Also note that the interface only defines the ability to get the Name property, not to set it. So, although you can of course set the Name on Employee and Animal instances, you won't be able to do so from inside GenericComparer.
With the definitions above, here is an example of how you could write the GenericComparer<T>.Equals method:
public bool Equals(T x, T y)
{
// Just a convention: if any of x or y is null, let's say they are not equal
if (x == null || y == null) return false;
// Another convention: let's say an animal and an employee cannot be equal
// even if they have the same name
if (x.GetType() != y.GetType()) return false;
// And now if 2 employees or animals have the same name, we'll say they are equal
if (x.Name == y.Name) return true;
return false;
}
All that said, I don't know exactly what your use case for GenericComparer<T> is (maybe you need this in a Dictionary or some other method that requires a way to compare instances... Anyway, as other commenters have stated, the proper way to go would probably be to:
override Equals and GetHashCode
provide == and != operators
implement IEquatable<T>
Do this on all classes you want to have a custom implementation of equality.
If you happen to use a recent version of Visual Studio (I'm using VS 2019 16.8 at the moment), you can automatically do all of this by right-clicking the class name, choosing Quick Actions and Refactorings > Generate Equals and GetHashCode. You will be presented with a window that allows you to choose the properties that should be part of the equality logic and you can also optionally choose to implement IEquatable<T> and generate == and !=. Once done, I'd recommend you review the generated code and make sure you understand what it does.
By the way, if you do so, you'll notice that the generated code uses EqualityComparer<Employee>.Default. This is pretty much what you tried to implement by yourself with your GenericEqualityComparer.
Now to the part of your questions relating to reference types. I'm not sure I understand your questions for I don't see an obvious link between generic types and references. Generic types can act just as well on both reference and value types.
What bothers you is maybe the fact that equality does not work the same way in reference and value types:
For reference types, the default way the compiler considers things equal is to look at their references. If the references are the same, then the things are considered the same. To put it differently suppose you create 2 instances of an Employe class and feed them with exactly the same Name and Salary. Because they are distinct objects (with distinct places in memory, that is different references), emp1 == emp2 will return false.
In the case of value types (suppose Employee is a struct and not anymore a class), the compiler does something else: it compares all the properties of the struct and decides based upon their content whether the 2 employees are equal or not. In this case, emp1 == emp2 will return true. Note that here, the compiler (or rather the .NET runtime) is doing something similar to what you attempt to do with your universal comparer. However, it only does so for value types and it is rather slow (this is why one should often implement IEquatable and override Equals and GetHashcode on structures).
Well, I'm not sure I've answered all your questions, but if you want to know more, you should definitely go through some C# tutorials or documentation to understand more about reference vs value types and equality (even before you jump into implementing your own generic types).
What you're expecting of generics just isn't right:
I was thinking that since List<T> can accept anything like List<Employee> or List<Animal> or anything then why my GenericComparer cannot.
foreach(var employee in employees)
{
string name = employee.Name; //Here we are able to access the properties
}
Here you say that you can access the properties, but that's specifically because the list employees was instantiated as List<Employee>. The implementation of List however could Not access those properties, because it only sees T !
There are some ways to achieve what you want, but you'd have to consider design and performance pros/cons depending on your specific use-case and needs.
Here's what you could do:
public abstract class CustomComparable
{
// Force implementation to provide a comparison value
public abstract object GetComparisonValue();
}
public class Employee: CustomComparable
{
public int Department { get; set; }
public override object GetComparisonValue()
{
return Department;
}
}
public class Animal : CustomComparable
{
public string Zoo { get; set; }
public override object GetComparisonValue()
{
return Zoo;
}
}
public class CustomComparer<T> : IEqualityComparer<T> where T: CustomComparable
{
public bool Equals(T x, T y)
{
return x != null && y != null && x.GetComparisonValue().Equals(y.GetComparisonValue());
}
public int GetHashCode(T obj)
{
return obj.GetComparisonValue().GetHashCode();
}
}
Then you'd get this, for example:
class Program
{
static void Main(string[] args)
{
Animal cat = new Animal() { Zoo = "cat zoo" };
Animal dog = new Animal() { Zoo = "dog zoo" };
Animal puppy = new Animal() { Zoo = "dog zoo" };
List<Animal> animals = new List<Animal>() { cat, dog, puppy };
CustomComparer<Animal> animalComparer = new CustomComparer<Animal>();
Console.WriteLine($"Distinct zoos ? {animals.Distinct(animalComparer).Count() == animals.Count}");
Employee bob = new Employee() { Department = 1 };
Employee janet = new Employee() { Department = 2 };
Employee paul = new Employee() { Department = 3 };
List<Employee> employees = new List<Employee>() { bob, janet, paul };
CustomComparer<Employee> employeeComparer = new CustomComparer<Employee>();
Console.WriteLine($"Distinct departments ? {employees.Distinct(employeeComparer).Count() == employees.Count}");
}
}
> Distinct zoos ? False
> Distinct departments ? True
With all that being said, do use IEquatable<T>. The specific use of such equality implementations seems out of the scope of your initial question however, and many other resources and Q&A's can help you with these.
What you describe is easily possible, just use delegates.
public class GenericComparer<T, TValue> : IEqualityComparer<T>
{
private Func<T, TValue> _getter;
private IEqualityComparer<TValue> _valueComparer;
public GenericComparer(Func<T, TValue> getter, IEqualityComparer<TValue> valueComparer = null)
{
_getter = getter;
_valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
}
public bool Equals(T x, T y)
{
return _valueComparer.Equals(_getter(x), _getter(y));
}
public int GetHashCode(T obj)
{
return _valueComparer.GetHashCode(_getter(obj));
}
}
To use it, simply tell it by which property you want it to compare:
var animalByNameComparer = new GenericComparer<Animal, string>(an => an?.Name);
Of course, you can also handle the null case already in the generic comparer, also.
To your second question
The simple difference between List<T> and List<Employee> is that the former is an open generic type while the latter is a closed generic type. If you define a generic type, you always define it as an open generic type, so the compiler does not know what T will be. If you consume a generic type, you often work with closed generic types, i.e. the placeholder T already has a value and therefore, the compiler is able to resolve a symbolic name like .Name to a property. If you use the generic type in an open generic method (a method with a type parameter), you also cannot bind symbols.

Converting Generic List of object to defined collection class with Lambda and C#

This is an issue I have seen in two different jobs that use a 3-tier structure and haven't found a clean way around it. This also applies to using LINQ statements I believe.
I have 2 classes, one is an object and the other is a defined collection of those objects that might have some additional functionality in it:
public class TestObject
{
public Int32 id {get; set;}
public string value {get; set;}
}
public class TestObjectCollection: List<TestObject>
{
public TestObject Get(Int32 id)
{
return this.FirstOrDefault(item => item.id==id);
}
}
Say I use a lambda expression like:
List<TestObject> result = data.Where(item => item.id > 0).ToList();
Is there an easy way to convert that list of objects to my defined collection without doing something like this:
TestObjectCollection resultAsCollection = new TestObjectCollection()
resultAsCollection.AddRange(result);
It seems like there should be a way to cast my GenericList returned by the Lambda expression to my TestObjectCollection without the added step looping through my returned results.
No, there isn't. ToList creates a List<T> - and there's no way of casting a plain List<T> to a TestObjectCollection without creating a new TestObjectCollection.
Personally I'd avoid creating a collection deriving from List<T> at all (I'd almost always use composition instead) but if you really want to have that collection, the simplest approach is to create your own extension method:
public static class TestEnumerable
{
public static TestObjectCollection ToTestObjectCollection(this IEnumerable<TestObject> source)
{
return new TestObjectCollection(source);
}
}
... and implement the appropriate constructor, of course, which can probably just chain to the List(IEnumerable<T>) constructor. Then you can write:
var resultAsCollection = data.Where(item => item.id > 0).ToTestObjectCollection();
It seems like there should be a way to cast my GenericList returned by
the Lamda expression to my TestObjectCollection without the added step
looping through my returned results
There isn't because with generic collections you can use covariance and not contravariance.
Other than Jon's solution (which is good) you can create constructor overload:
public class TestObjectCollection: List<TestObject>
{
public TestObjectCollection(IEnumerable<TestObject> list) { AddRange(list);}
...
}
Usage:
var resultAsCollection = new TestObjectCollection(data.Where(item => item.id > 0));

How much of a performane hit will i take from casting when trying to make this code mistake proof?

I have the following code:
public class CrudModel<T> : ICrudModel<T> where T : DomainBase
{
public IQueryable<T> GetAll()
{
return Repository.Query<T>();
}
}
the issue is that some of the objects (T) I need to do an extra filter so I created a separate method like this:
public IEnumerable<TR> GetAllwithinOrg<TR>() where TR : DomainBase, IFilterable
{
var items = Repository.Query<TR>();
return FilterwithinOrg(items);
}
where filter method looks like this:
public IEnumerable<TResult> FilterwithinOrg<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
this all works fine but the issue is that I have to remember to call method 1 or method 2 (based on if the object supports the IFilterable interface
On this question, I got the suggestion to do this:
public IQueryable<T> GetAll()
{
var items = Repository.Query<T>();
if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
items = FilterwithinOrg(items.Cast<IFilterable>()).Cast<T>().AsQueryable();
}
return items;
}
so I can support both use cases in one method. This seems to work but I am trying to understand what type of performance hit that I am going to take by doing this
items.Cast<IFilterable>()).Cast<T>().AsQueryable()
If it's bad then I will deal with remembering to call 2 separate methods from the outside but obvious it would be convenient to just have one. Any suggestions?
I think I will leave it in just as a backup if I forget to call the second method but wanted to again see if I can keep it to just one if possible to make it simpler for the outside caller.
How about having another method with where clause in the CrudModel class.
public IEnumerable<T> GetAll<T>(Func<T, bool> whereClause) where T : DomainBase
{
var items = Repository.Query<T>();
return items.Where(whereClause);
}
And call using
List<int> intList = new List<int>() { 1 };
intList.GetAll<int>((i) => sampledict.ContainsKey(i));
I felt it is not proper to make things complex by having logic cramped into one single GetAll method and since CrudModel seems to be generic, better to have generic method that accepts any condition.
First, I think it is a bit strange that you have a function GetAll, but for certain types, you start filtering, resulting in not getting all :)
Besides that, I do not think you have big performance loss... In essense you do one extra check inside you GetAll-method: typeof(IFilterable).IsAssignableFrom(typeof(T)) is like an ordinary cast. You will hardly feel it.
Perhapse the filter itself could be improved. You create a dictionary. Does the dictionary have the same values every call, or does it change. And why a dictionary if you only use the keys, and not the values? What about a HashSet<T>?
Casting time can be neglected comparing to the time spent for database query. However, you are querying the whole table and filter in-memory according to your code here:
public IEnumerable<TResult> FilterwithinOrg<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
var dict = GetDict();
return linked.Where(r => dict.ContainsKey(r.Id));
}
Remember that you need to filter query, not list, so you should change the method to accept and return IQueryable<TResult> instead like:
public IQueryable<TResult> FilterwithinOrg<TResult>(IQueryable<TResult> linked) where TResult : IFilterable
{
var dictKeys = GetDict().Keys.ToList();
return linked.Where(r => dictKeys.Contains(r.Id));
}
Noted that the filter expression has to have equivalent SQL expression or runtime-error will occur.

Can one copy the contents of one object to another dynamically if they have the same interface?

For example, if I have two objects, one which is of type Monkey and the other of type Dog, and they both implement IAnimal, which is something like this:
interface IAnimal
{
int numberOfEyes {get; set;}
string name {get; set;}
}
I want to do something like this:
Monkey monkey = new Monkey() { numberOfEyes = 7, name = "Henry" };
Dog dog = new Dog();
MyFancyClass.DynamicCopy(monkey, dog, typeof(IAnimal));
Debug.Assert(dog.numberOfEyes == monkey.numberOfEyes);
I imagine one can create a class like MyFancyClass using reflection... any clever person have an idea?
Thanks,
Stephen
Just to throw it in the mix... you can also use AutoMapper to map/copy one object to another.... they don't even have to implement the same interface. To make it work automagically, just the names of the properties have to match and then you just do something like:
Mapper.Map<IAnimal, MyClass>(myInstance);
A reflection based solution follows. Note that the reflection work is done only once per Type and then cached, so the overhead should be minimal. Will work with .NET 3.5 and it is not restricted to interfaces.
Note that I use reflection to get all the properties on type T and filter to the properties that have both getters and setters. I then build an expression tree for each property that retrieves the value from the source and assigns that value to the target. The expression trees are compiled and cached in a static field. When the CopyProperties method is called, it invokes the copier for each property, copying all the properties defined in type T.
// Usage
Monkey monkey = new Monkey() { numberOfEyes = 7, name = "Henry" };
Dog dog = new Dog();
DynamicCopy.CopyProperties<IAnimal>(monkey, dog);
Debug.Assert(dog.numberOfEyes == monkey.numberOfEyes);
...
// The copier
public static class DynamicCopy
{
public static void CopyProperties<T>(T source, T target)
{
Helper<T>.CopyProperties(source, target);
}
private static class Helper<T>
{
private static readonly Action<T, T>[] _copyProps = Prepare();
private static Action<T, T>[] Prepare()
{
Type type = typeof(T);
ParameterExpression source = Expression.Parameter(type, "source");
ParameterExpression target = Expression.Parameter(type, "target");
var copyProps = from prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
where prop.CanRead && prop.CanWrite
let getExpr = Expression.Property(source, prop)
let setExpr = Expression.Call(target, prop.GetSetMethod(true), getExpr)
select Expression.Lambda<Action<T, T>>(setExpr, source, target).Compile();
return copyProps.ToArray();
}
public static void CopyProperties(T source, T target)
{
foreach (Action<T, T> copyProp in _copyProps)
copyProp(source, target);
}
}
}
Copy constructor is what I usually do:
class Monkey : IAnimal
{
public Monkey(IAnimal other)
{
//Copy properties here...
}
}
You have several options here:
You could go down the route of using reflection, but this will be much slower than other options, and you'll have to craft yoiur refleciton code. To make nice generic "clone" code using reflection is non-trivial, especially when you have to start catering for objects that contain lists/arrays/dictionaries of other object instances.
A copy constructor, as Dr Herbie mentioned, is one option.
Another would be to implement ICloneable on all your types (you could make you interface implement ICloneable to force all IAnimals to implement it). This may not be dynamic, like reflection (you'd have to hand craft it for each class), but assuming you just copy the property values accross, it'll be way faster than reflection.
Also worth thinking about is immutability. If you can make your concrete types immutable (using readonly on all fields so they can't be changed), then you probably don't need to worry about cloning at all. Everything can happily share the same instance safe in the knowledge that no other sharer can be modifying it in any way. This sort of immutability can be very powerful, although you need to be careful if your interface contains collections/arrays that can be modified.
Finally, if you have a lot of classes, you could look at code generation to generate C# "cloner" classes (whose job it is to generate a clone of a given type) and compile them into an assembly. You can use reflection here to create the "cloner class template", but since it generates code (that compiles with the rest of your project), you don't have the run-time hit of slow reflection.
So, there are lots of options for cloning - but using reflection, even though it can be naice and dynamic, is often not the best approach.
You could maKe IAnimal Implement ICloneable. Then do a memeberwise clone on the monkey or an other class that implements ICloneable. This is a shallow copy by the way.
public interface IAnmial : ICloneable
{
string Name{get; set;}
object Clone();
}
public class Monkey : IAnmial
{
public string Name{get; set;}
public object Clone()
{
return this.MemberwiseClone();
}
}
public class Dog : IAnmial
{
public string Name{get; set;}
public object Clone()
{
return this.MemberwiseClone();
}
}
public class Test()
{
public void CloneAnimal()
{
Dog dog = new Dog()
{
Name = "MyAnimal",
};
IAnimal monkey = dog.Clone() as IAnimal;
}
}
As long as DynamicCopy takes in an IAnimal as a method parameter you can do that.
But it really helps to understand what you are trying to do.
Why not just implement a method in IAnimal?
(EDIT: As commenters have helpfully pointed out, IAnimal should be converted into an abstract base class Animal for this solution. This makes sense anyway, since the whole inference behind this question is that the child classes contain properties defined in the parent.)
// In Aminal class.
public void CopyAttributes(Animal source)
{
this.numberOfEyes = source.numberOfEyes;
this.name = source.name;
}
Doing something like this via reflection gets messy quick. Should you only copy properties? What about get/set methods? What about read-only properties? That is why it is probably best to define the behavior you actually want at each level. You can then override at lower levels if desired.
// In Monkey class.
public void CopyAttributes(Monkey source)
{
super.CopyAttributes(source);
this.numberOfTails = source.numberOfTails;
}
Try looking at struct methods

Method chaining generic list extensions

I have a List of a "complex" type - an object with a few string properties. The List itself is a property of another object and contains objects of a variety of types, as shown in this abbreviated class structure:
Customer {
public List<Characteristic> Characteristics;
.
.
.
}
Characteristic {
public string CharacteristicType;
public string CharacteristicValue;
}
I'd like to be able to collect a List of the values of a given type of Characteristics for the current Customer, which I can do in a 2-step process as follows:
List<Characteristic> interestCharacteristics = customer.Characteristics.FindAll(
delegate (Characteristic interest) {
return interest.CharacteristicType == "Interest";
}
);
List<string> interests = interestCharacteristics.ConvertAll<string>(
delegate (Characteristic interest) {
return interest.CharacteristicValue;
}
);
That works fine, but it seems like a long way around. I'm sure I must be missing a simpler way of getting to this list, either by chaining together the FindAll() and Convert() methods, or something else I'm overlooking entirely.
For background, I'm working in .Net 2.0, so I'm limited to the .Net 2 generics, and the Characteristic class is an external dependency - I can't change it's structure to simplify it, and there are other aspects of the class that are important, just not in relations to this problem.
Any pointers or additional reading welcomed.
Here's a generator implementation
public static IEnumerable<string> GetInterests(Customer customer)
{
foreach (Characteristic c in customer.Characteristics)
{
if (c.CharacteristicType == "Interest")
yield return c.CharacteristicValue;
}
}
sadly 3.5 extension methods and lambda are out based on your requirements but for reference here's how to do it:
customer.Characteristics
.Where(c => c.CharacteristicType == "Interest")
.Select(c => c. CharacteristicValue);
I would do some of the work manualy. By doing a FindAll first, and then a Convert, you're looping through your collection twice. It doesn't seem neccessary. If all you want at the end of the day, is a List of CharacteristicValue then just loop through your original collection, and add the CharacteristicValue to a List of each one that matches your criteria. Something like this:
Predicate<Characteristic> criteria = delegate (Characteristic interest)
{
return interest.CharacteristicType == "Interest";
};
List<string> myList = new List<string>();
foreach(Characteristic c in customer.Characteristics)
{
if(criteria(c))
{
myList.Add(c.CharacteristicValue);
}
}
Why not create a Dictionary<string, List<string>>, that way you can add "Interest" as the key, and a list of values as the value. For example:
Customer {
public Dictionary<string, List<string>> Characteristics;
.
.
.
}
...
Characteristics.Add("Interest", new List<string>());
Characteristics["Interest"].Add("Post questions on StackOverflow");
Characteristics["Interest"].Add("Answer questions on StackOverflow");
..
List<Characteristic> interestCharacteristics = Characteristics["Interest"];
Furthermore, if you wanted, you could limit your characteristics to a list of possible values by making it an enum, then use that as the data type of your dictionary's key:
public enum CharacteristicType
{
Interest,
Job,
ThingsYouHate
//...etc
}
then declare your dictionary as:
public Dictionary<CharacteristicType, List<string>> Characteristics;
..
Characteristics.Add(CharacteristicType.Interest, new List<string>());
Characteristics[CharacteristicType.Interest].Add("Post questions on StackOverflow");
Characteristics[CharacteristicType.Interest].Add("Answer questions on StackOverflow");

Categories

Resources