I need to send different IEnumerables to an Printer
object.
This printer object will then do something to them, inside a foreach loop.
class Printer
{
public Printer(IEnumerable list)
{
foreach (var enumerable in list)
{
//DO STUFF
}
}
}
This lets me send any enumerable, such as an List<T>
to the printer object.
such as
var list = new List<string> {"myList"};
new Printer(list); //mylist
This works fine.
BUT if I send a Dictionary<T, T>
such as:
var dictionary = new Dictionary<int, string> {{1, "mydict"}};
new Printer(dictionary); //[1, mydict]
It'll have a key and a value. What I would want though, would be separate access to the Value
prope开发者_如何学JAVArty inside the foreach
loop. All I DO have access to is the enumerable object, which has no properties I can use.
Now what if the datatype T is an object containing several properties (this goes for both examples). How would I be able to use these properties in my foreach loop?
Do I honestly have to create an overload of the constructor, foreach possible datatype I might send down to it?
Also, all I need to do in the foreach is not dependable to any datatypes - as it won't manipulate everything. I do need ACCESS to all the properties though.
Also, this is just example code, not actually the production-code I use in my application.
Can you change the code of the Printer class? If it accepted something like an IEnumerable<IPrintable>
instead of just an IEnumerable
it would be easier. With an interface like this:
interface IPrintable
{
void Print();
}
Then all objects that would be sent to the Printer
would need to implement that interface. Then you could do:
class Printer
{
public Printer(IEnumerable<IPrintable> list)
{
foreach (var enumerable in list)
{
enumerable.Print();
}
}
}
And if you have a dictionary of printable objects, something like:
var dict = new Dictionary<int,IPrintable>();
You could just pass the values to the function:
var printer = new Printer(dict.Values);
You could modify your method to accept a delegate that returns the data the print method needs. Something like this:
// 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.
}
}
}
Usage:
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 });
Per Erik Stendahl's answer is very similar.
You have to extract an enumerable with the values you want to pass before you call new Printer(). In the case of the dictionary this is simple: just use dict.Values. A more general case is:
var list = List<MyObject>()...
var printer = new Printer(list.Select(x => x.MyProperty));
If you want to treat different types differently, you probably should make different methods. If you want to treat them the same, you should accept a common interface, and only use the methods defined for the interface.
It would be possible to do
if (list is Dictionary<int, string>) {
// do special case
}
but I shudder at the thought.
You can even check generically:
class Printer<T>
{
public Printer<T>(IEnumerable list)
{
foreach (var enumerable in list)
{
if (list is Dictionary<T, T>) {
//DO STUFF
}
}
}
}
The problem is that a collection, though it is enumerable, can hold different types of objects, as you saw with the difference between the List
and the Dictionary
.
To get around this without coding for each object type, you'd have to only accept an enumerable collection of a certain type that you define, for example IEnumerable<IMyType>
.
If you can't do anything at the callee, it's up to the caller to make sure it passes an IEnumerable
that is "valid" for Printer, like passing dictionary.Values
instead of dictionary
in your example. However if the class is public and will be used by 3rd party users, you're better to add some generic constraint to your IEnumerable
, as others stated.
Here is the result: I used your guys help, so I guess I shouldn't vote my own as the answer.
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'd naturally need to implement custom GetEnumerator() foreach object - no problem though!
精彩评论