List all IEnumerables
I need to send various IEnumerables to an object Printer
. This printer object will then do something to them inside the foreach loop.
class Printer
{
public Printer(IEnumerable list)
{
foreach (var enumerable in list)
{
//DO STUFF
}
}
}
This allows me to forward any objects such as List<T>
to a printer object. such as
var list = new List<string> {"myList"};
new Printer(list); //mylist
This works great. BUT if I post a Dictionary<T, T>
, for example:
var dictionary = new Dictionary<int, string> {{1, "mydict"}};
new Printer(dictionary); //[1, mydict]
It will have a key and a value. However, I would like to have separate access to the property Value
inside the loop foreach
. All I have access to is an enumerable object that has no properties that I can use.
Now what if the datatype T is an object containing multiple properties (this applies to both examples). How can I use these properties in my foreach loop?
Should I honestly create a constructor overload, for some possible data type I could send it?
Also, everything I need to do in the foreach is independent of any data types - since it won't manipulate everything. I really need ACCESS for all properties.
Also, this is just sample code, not production code that I am using in my application.
source to share
Can you change the code for the Printer class? If he took something like IEnumerable<IPrintable>
instead of simple IEnumerable
, it would be easier. With an interface like this:
interface IPrintable
{
void Print();
}
Then all objects that will be sent Printer
to will have to implement this interface. Then you can do:
class Printer
{
public Printer(IEnumerable<IPrintable> list)
{
foreach (var enumerable in list)
{
enumerable.Print();
}
}
}
And if you have a dictionary for printable objects, something like:
var dict = new Dictionary<int,IPrintable>();
You can simply pass the values ββto the function:
var printer = new Printer(dict.Values);
source to share
You can change your method to accept a delegate that returns the data the print method needs. Something like that:
// You will not need this class, if you always want a single string result.
class PrinterData
{
public string Value { get; set; }
// More properties?
}
class Printer
{
public Printer<T>(IEnumerable<T> list, Func<T, PrinterData> func)
{
foreach (T item in list)
{
PrinterData data = func(item);
// Do something with the data.
}
}
}
Using:
int[] ints = new int[] {1,2,3};
new Printer().Print(ints, x => new PrinterData() { Value = x.ToString() });
var dictionary = new Dictionary<int, string> {{1, "mydict"}};
new Printer().Print(dictionary, x => new PrinterData() { Value = x.Name + " = " + x.Value });
Eric Stendhal's answer is very similar. source to share
If you want to handle different types differently, you should probably do different methods. If you want to treat them the same, you must accept the generic interface and only use the methods defined for the interface.
Could be done
if (list is Dictionary<int, string>) {
// do special case
}
but I shudder at the thought.
You can even check in general terms:
class Printer<T>
{
public Printer<T>(IEnumerable list)
{
foreach (var enumerable in list)
{
if (list is Dictionary<T, T>) {
//DO STUFF
}
}
}
}
source to share
The problem is that a collection, although enumerable, can contain different types of objects, as you saw with the difference between List
and Dictionary
.
To get around this without coding for each object type, you would only need to accept an enumerable collection of a specific type that you define for example IEnumerable<IMyType>
.
source to share
If you cannot do anything at the called party, make sure that he is transmitting IEnumerable
which is "valid" for the printer, for example, transmit dictionary.Values
instead dictionary
in your example. However, if the class is public and will be used by third party users, you'd be better off adding some general constraint to yours IEnumerable
as pointed out by others.
source to share
Here's the result: I used your guys, so I guess I shouldn't vote on my own.
class Printer
{
public Printer(IEnumerable<IPrintable> list) //Accepts any collection with an object that implements IPrintable interface
{
foreach (var enumerable in list) //iterate through list of objects
{
foreach (var printable in enumerable)//loops through properties in current object
{
//DO STUFF
}
}
}
}
interface IPrintable : IEnumerable { }
class SomeObject : IPrintable
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public interface IEnumerable
{
IEnumerator GetEnumerator(); //Returns a Enumerator
}
public IEnumerator GetEnumerator()
{
yield return Property1;
yield return Property2;
}
}
I would naturally need to implement my own GetEnumerator () foreach object - no problem!
source to share