How to access the methods of a generic ObservableCollection? - c#

public class Car
{
public ObservableCollection<int> ints = new ObservableCollection<int> { 12, 3, 4, 1, 2 };
public ObservableCollection<double> doubles = new ObservableCollection<double> { };
public ObservableCollection<string> strings = new ObservableCollection<string> { 12, 3, 4, 1, 2 };
public ObservableCollection<Color> colors = new ObservableCollection<Color> { ColorA, ColorB, ColorC };
}
Let's say I have a class call Car,
inside the class, I have 4 lists.
In another class, I would like use Reflection to call a Linq method to find if these four lists contain anything.
public void FindProperties()
{
foreach (var prop in typeof(Car).GetProperties())
{
if (prop.PropertyType == typeof(ObservableCollection<T>))
{
var list = (ObservableCollection<T>)prop.GetValue(Car);
if (list.Any())
{//Do something}
}
}
}
Since ObservableCollection< T > isn't a thing, the code is not working, but hopefully it explains what I attempt to do.
Inside the Car class I actually have 40+ of these lists and I really don't want to do a switch and case cast to do that. It doesn't seem very clean and smart to do that either.
I don't need to know what is T, I just need to know if the list contains anything and maybe use the CollectionChanged event as well. What is the best way to do it apart from casting each field?

Related

Initializing an array of generic collections with each a different generic argument

During the development of one of my projects, I encountered an issue regarding generic types.
The project requires me to write a class that would act as a source of list objects. Suppose I had the following class:
public class TablesProvider
{
private readonly List[] _tables;
public TablesProvider()
{
// initialize the tables var here....
}
public List<TItem> GetTable<TItem>()
{
return (List<TItem>)_tables.Single(x => x is List<TItem>);
}
}
This class obviously doesn't work, because the List type is a generic type and therefore the generic arguments should be specified.
So I made an abstract type called MyList, that would be derived by a more specific type MyList<TItem> in order to escape this requirement, and edited the TablesProvider a little.
public class TablesProvider
{
private readonly MyList[] _tables;
public TablesProvider()
{
// initialize the tables var here....
}
public MyList<TItem> GetTable<TItem>()
{
return (MyList<TItem>)_tables.Single(x => x is MyList<TItem>);
}
}
public abstract class MyList
{
// ...
}
public class MyList<TItem> : MyList, IList<TItem>
{
private readonly List<TItem> _elements = new List<TItem>();
public TItem this[int index]
{
get { return _elements[index]; }
set { _elements[index] = value; }
}
// ...
}
This works quite well. There is only one problem left. Suppose I had 45 different collections, each defined with a different generic argument. What would be the best way of initializing all of those collections? I cannot use a for loop here, since generic parameters are specified at compile-time and not at runtime, and therefore a construction like this wouldn't be possible:
for (int i = 0; i < 45; i++)
_tables[i] = new MyList<GenericParameters[i]>();
My ultimate goal is to have the luxury to just do something like this...
var table = _tablesProvider.GetTable<SomeClass>();
var element = table[3];
var propertyValue = element.SomeProperty;
... without the need to cast the variable element in order to access its type-specific members.
It is probably worth mentioning that the amount of different list objects is fixed to 45. This will not change. In theory, I could initialize the array line by line, or have 45 properties or variables instead. Both of these options, however, sound as a rather cheap solution to me, but I will accept one of them if there is no other way.
Any of you got some ideas? Am I doing this completely wrong? Should I consider an other structure?
Thanks in advance.
Yes, it is possible to do what you are describing if you use reflection.
Supposing that your hypothetical GenericParameters array is an array of Types (since you can't have an array of type identifiers), you can define this helper function:
private MyList MakeList(Type t)
{
return (MyList)Activator.CreateInstance(typeof(MyList<>).MakeGenericType(t));
}
And that will allow you to do this:
public TablesProvider()
{
var GenericParameters = new[] { typeof(string), typeof(int), typeof(DateTime) };
_tables = new MyList[GenericParameters.Length];
for (int i = 0; i < GenericParameters.Length; i++)
{
_tables[i] = MakeList(GenericParameters[i]);
}
}
You can even use LINQ if you want:
public TablesProvider()
{
var GenericParameters = new[] { typeof(string), typeof(int), typeof(DateTime) };
_tables = GenericParameters.Select(MakeList).ToArray();
}
Previous answer:
Well, the reality is that you're going to have a list of 45 different types somewhere, which pretty much means you're going to have 45 different lines of similar code. So one could say the goal is to make those lines as concise as possible.
One way to do so would be to add a helper function:
private void AddTable<T>()
{
_tables.Add(new MyTable<T>());
}
(this assumes changing _tables to a List<MyTable>)
Then you could just do:
AddTable<Type1>();
AddTable<Type2>();
AddTable<Type3>();
AddTable<Type4>();
this implementation works
public class TablesProvider
{
private readonly List<object> _tables;
public TablesProvider()
{
_tables = new List<object>();
}
public IList<TItem> GetTable<TItem>()
{
var lst = (List<TItem>)_tables.SingleOrDefault(x => x is List<TItem>);
if (lst == null)
{
lst = new List<TItem>();
_tables.Add(lst);
}
return lst;
}
}
it creates List of TItem when necessary; next time it returns the same list for TItem. it is lazy initialization
so you can do invoke
var table = _tablesProvider.GetTable<SomeClass>();
without any code like this:
for (int i = 0; i < 45; i++)
_tables[i] = new MyList<GenericParameters[i]>();
it is not ThreadSafe

My array class in C#

I'm trying to do this with Dictionary, Array seems more general so I'm using it as an example.
Class MyArray
{
private Array array;
public Array [p] // a property
{
get {return array[p]};
set
{
array[p] = value;
more_stuff();
}
}
}
This is sudo-code. I've included only a part of the class, where my problem would be. Can I use a property as above, or another structure to achieve this?
(new MyArray[]{4, 3, 1, 5})[2] = 4;
You're looking for an indexer.
So you're class should look like this:
class MyArray<T>
{
private T[] array = new T[100];
public T this[int p]
{
get
{
return array[p];
}
set
{
array[p] = value;
// more_stuff();
}
}
}
To be able to use a collection initializer (e.g. new MyArray<int>{4, 3, 1, 5}), your class has to implement IEnumerable and provide a Add method.

Property Accessors on List<T> in .NET

I have a property called Fruits which contains a comma delimited string in the form "apples,bananases,peaches"
I want to make a list in the same class which makes the Fruits property easier to manipulate. Accessors won't work as they are not supported on lists or so it seems.
Basically i want a property called FruitList which auto populates based on the Fruits Property and when adding items or manipulating the FruitList it should auto populate the fruits property.
I need the Fruits Property for Entity framework.
You can invert the logic:
List<string> fruitsList = new List<string>();
public List<string> FruitsList
{
get
{
return fruitsList;
}
}
public string Fruits
{
get
{
return string.Join(',', fruitsList);
}
set
{
// Incomplete, does not handle null
FruitsList.Clear();
FruitsList.AddRange(value.Split(','));
}
}
You don't need to worry about updating Fruits if Fruits is determined by looking at FruitsList. You mention that you need Fruits as a string property for Entity Framework, but EF does not care whether it is backed by a string field.
The only realistic way to do this is to use a collection which can be observed for changes, and handle the event raised when it is changed, and update the property.
Something like ObservableCollection<T> would fit the bill.
example:
public class MyObject
{
public string Fruits{get;set;}
public IList<string> FruitList
{
get
{
var list = new ObservableCollection<string>(Fruits.Split(','));
list.CollectionChanged += (s,ea) => {
var items = (IList<string>)s;
Fruits = String.Join(",",items);
};
return list;
}
}
}
Usage:
var obj= new MyObject(){ Fruits="Apple,Banana,Orange" };
var list = obj.FruitList;
list.Add("Satsuma");
list.Add("Grapes");
list.Remove("Apple");
Console.WriteLine(obj.Fruits); // Output: Banana,Orange,Satsuma,Grapes
Live example: http://rextester.com/KCT33825
Having seen the concept here work, it's worth noting that the above implementation is frought with a bit of danger. It creates a new ObservableCollection every time you call the get accessor on it, which could have some unintended consequences.
For example, if you add the following line just before my original Console.WriteLine:
Console.WriteLine("{0}", obj.FruitList == list);
It outputs false which might seem strange as you might (and notionally, should) expect list and obj.FruitList to point to the same list.
You can get round this by changing the implementation to create only ever 1 ObservableCollection and always returning that from the get accessor:
public class MyObject
{
private string fruits;
private ObservableCollection<string> fruitList;
public string Fruits
{
get{ return this.fruits; }
set
{
this.fruits = value;
this.fruitList = CreateFruitList();
}
}
private ObservableCollection<string> CreateFruitList()
{
var list = new ObservableCollection<string>(this.fruits.Split(','));
list.CollectionChanged += (s,ea) => {
var items = (IList<string>)s;
this.fruits = String.Join(",",items);
};
return list;
}
public IList<string> FruitList
{
get
{
return fruitList;
}
}
}
Now all is right with the world again!
here's what you could do, create a proxy for your comma separated list:
public class MyClass
{
public string Fruits {get;set;}
public string [] FruitList {
get { return Fruits.Split(new [] {','}); }
//warning, the setter is dangerous
set { Fruits = string.Join(',', value); }
}
}
When I say the setter is dangerous, I only mean that if you change one element of the array, the Fruit won't be updated. It'll only be updated if you push a new array. If you need that behavior, consider implementing it using an ObservableCollection

Why can I initialize a List like an array in C#?

Today I was surprised to find that in C# I can do:
List<int> a = new List<int> { 1, 2, 3 };
Why can I do this? What constructor is called? How can I do this with my own classes? I know that this is the way to initialize arrays but arrays are language items and Lists are simple objects ...
This is part of the collection initializer syntax in .NET. You can use this syntax on any collection you create as long as:
It implements IEnumerable (preferably IEnumerable<T>)
It has a method named Add(...)
What happens is the default constructor is called, and then Add(...) is called for each member of the initializer.
Thus, these two blocks are roughly identical:
List<int> a = new List<int> { 1, 2, 3 };
And
List<int> temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
List<int> a = temp;
You can call an alternate constructor if you want, for example to prevent over-sizing the List<T> during growing, etc:
// Notice, calls the List constructor that takes an int arg
// for initial capacity, then Add()'s three items.
List<int> a = new List<int>(3) { 1, 2, 3, }
Note that the Add() method need not take a single item, for example the Add() method for Dictionary<TKey, TValue> takes two items:
var grades = new Dictionary<string, int>
{
{ "Suzy", 100 },
{ "David", 98 },
{ "Karen", 73 }
};
Is roughly identical to:
var temp = new Dictionary<string, int>();
temp.Add("Suzy", 100);
temp.Add("David", 98);
temp.Add("Karen", 73);
var grades = temp;
So, to add this to your own class, all you need do, as mentioned, is implement IEnumerable (again, preferably IEnumerable<T>) and create one or more Add() methods:
public class SomeCollection<T> : IEnumerable<T>
{
// implement Add() methods appropriate for your collection
public void Add(T item)
{
// your add logic
}
// implement your enumerators for IEnumerable<T> (and IEnumerable)
public IEnumerator<T> GetEnumerator()
{
// your implementation
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Then you can use it just like the BCL collections do:
public class MyProgram
{
private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 };
// ...
}
(For more information, see the MSDN)
It is so called syntactic sugar.
List<T> is the "simple" class, but compiler gives a special treatment to it in order to make your life easier.
This one is so called collection initializer. You need to implement IEnumerable<T> and Add method.
According to the C# Version 3.0 Specification "The collection object to which a collection initializer is applied must be of a type that implements System.Collections.Generic.ICollection for exactly one T."
However, this information appears to be inaccurate as of this writing; see Eric Lippert's clarification in the comments below.
It works thanks to collection initializers which basically require the collection to implement an Add method and that will do the work for you.
Another cool thing about collection initializers is that you can have multiple overloads of Add method and you can call them all in the same initializer! For example this works:
public class MyCollection<T> : IEnumerable<T>
{
public void Add(T item, int number)
{
}
public void Add(T item, string text)
{
}
public bool Add(T item) //return type could be anything
{
}
}
var myCollection = new MyCollection<bool>
{
true,
{ false, 0 },
{ true, "" },
false
};
It calls the correct overloads. Also, it looks for just the method with name Add, the return type could be anything.
The array like syntax is being turned in a series of Add() calls.
To see this in a much more interesting example, consider the following code in which I do two interesting things that sound first illegal in C#, 1) setting a readonly property, 2) setting a list with a array like initializer.
public class MyClass
{
public MyClass()
{
_list = new List<string>();
}
private IList<string> _list;
public IList<string> MyList
{
get
{
return _list;
}
}
}
//In some other method
var sample = new MyClass
{
MyList = {"a", "b"}
};
This code will work perfectly, although 1) MyList is readonly and 2) I set a list with array initializer.
The reason why this works, is because in code that is part of an object intializer the compiler always turns any {} like syntax to a series of Add() calls which are perfectly legal even on a readonly field.

Accessing object's property inside generic method [duplicate]

This question already has answers here:
Get Property from a generic Object in C#
(4 answers)
Closed 9 years ago.
How can access the property of an object inside generic method?
I can't use where T: A because this method will receive different objects, but all objects have a common property to work on.
(I also can't make for them a common interface)
public class A
{
public int Number {get;set;}
}
List<A> listA = new List<A>{
new A {Number =4},
new A {Number =1},
new A {Number =5}
};
Work<A>(listA);
public static void Work<T>(List<T> list1)
{
foreach(T item in list1)
{
do something with item.Number;
}
}
An update: I need also to set the property
You have a few choices:
Make a common interface.
Use reflection.
Use the dynamic type in .NET 4.
I know you said you can't do the first, but it's the best option for performance and maintainability so please reconsider if its possible before choosing one of the other methods. Remember that even if you can't modify the original code you might still be able to choose the first option. For example if your classes are partial classes you can implement the interface in another file:
File 1:
// Automatically generated code that you can't change.
partial class A
{
public int Number { get; set; }
}
File 2:
interface IHasNumber
{
int Number { get; set; }
}
partial class A : IHasNumber
{
}
If the original class isn't defined as partial you could write wrapper classes around them that implement the interface.
Once you have the common interface you can change your generic constraint to require this interface:
where T : IHasNumber
If you don't need the list - just the items, I would use a projection outside the method:
static void Main()
{
List<A> listA = new List<A>{
new A {Number =4},
new A {Number =1},
new A {Number =5}
};
Work(listA.Select(a => a.Number));
}
public static void Work(IEnumerable<int> items)
{
foreach (number item in items)
{
// do something with number;
}
}
If you need the list - a projection inside the method via a selector:
static void Main()
{
List<A> listA = new List<A>{
new A {Number =4},
new A {Number =1},
new A {Number =5}
};
Work(listA, a => a.Number);
}
public static void Work<T>(IList<T> list, Func<T, int> selector)
{
foreach (T obj in list)
{
int number = selector(obj);
// do something with number;
}
}

Categories

Resources