item - ienumerable int c#
Una clase con múltiples interfaces IEnumerable<T>: ¿Qué hacer con el método no genérico? (4)
Creo que entiendo por qué IEnumerable<T>
hereda de IEnumerable
, después de leer la publicación: ¿Por qué IEnumerable<T>
hereda de IEnumerable?
Sin embargo, no estoy seguro de cuál es la mejor manera de implementar el método no genérico al aplicar 2 interfaces genéricas. Aquí hay un ejemplo del código que estoy escribiendo:
public interface IComponentA { /* ... Interface A Code ... */ }
public interface IComponentB { /* ... Interface B Code ... */ }
public class ComponentModel: IEnumerable<IComponentA>, IEnumerable<IComponentB>
{
public ComponentModel() { }
private List<IComponentA> ListOfComponentA = new List<IComponentA>();
private List<IComponentB> ListOfComponentB = new List<IComponentB>();
// ... Some public methods to add and remove components (for A and B).
IEnumerator<IComponentA> IEnumerable<IComponentA>.GetEnumerator()
{
return ListOfComponentA.GetEnumerator();
}
IEnumerator<IComponentB> IEnumerable<IComponentB>.GetEnumerator()
{
return ListOfComponentB.GetEnumerator();
}
// The fact that IEnumerable<T> inherits from the non-generic IEnumerable
// now means I have to deal with this.
IEnumerator IEnumerable.GetEnumerator()
{
// Throwing a NotImplementedException is probably not a good idea
// so what should I put in here?
throw new NotImplementedException();
}
}
Las sugerencias de qué poner en el método no genérico son bienvenidas por favor.
La respuesta aceptada es exactamente correcta; debe implementar cosas que tengan dos secuencias que tengan dos secuencias, no como dos secuencias.
La razón dada hasta ahora acerca de por qué es una mala idea es suficiente, es decir, que generalmente es menos confuso cuando se elige la composición sobre la herencia. Sin embargo, creo que es valioso señalar que hay otra razón por la que implementar dos IEnumerable<T>
es una idea realmente mala: agregamos covarianza genérica a IEnumerable<T>
en C # 4.
La covarianza genérica significa que puede pasar una secuencia de tortugas a un método que toma una secuencia de animales, porque las tortugas son animales. Entonces, ¿qué hace entonces el tiempo de ejecución en este caso?
class Weird : IEnumerable<Turtle>, IEnumerable<Giraffe> { ... }
...
Weird weird = whatever;
foreach(Animal animal in weird)
{
...
¿Es animal
una tortuga o una jirafa? El tiempo de ejecución tiene que elegir uno de ellos, y no está claro cuál es el adecuado para elegir. Esta es una de las situaciones raras en las que la especificación de C # no dice lo que sucede; Este es un comportamiento definido por la implementación. Evitar, evitar, evitar.
Probablemente yo no haría eso, yo mismo. Puede ser confuso para un usuario hacer que el enumerador enumere sobre diferentes cosas dependiendo de la referencia de la interfaz que lo llame y, por supuesto, el problema de lo que devuelve el genérico.
En su lugar, solo podría exponer una versión ish de solo lectura de las listas como un iterador:
public class ComponentModel
{
public ComponentModel() { }
private List<IComponentA> ListOfComponentA = new List<IComponentA>();
private List<IComponentB> ListOfComponentB = new List<IComponentB>();
public IEnumerable<IComponentA> AComponents
{
get { return ListOfComponentA.Skip(0); }
}
public IEnumerable<IComponentB> BComponents
{
get { return ListOfComponentB.Skip(0); }
}
...
}
Al utilizar Skip(0)
, devuelve un iterador, lo que evita que vuelvan a la List<IComponentA>
y modifiquen la List
desde abajo.
Por supuesto, también puedes usar un ReadOnlyCollection
, pero son un poco torpes ya que se lanzan cuando intentas mutar operaciones.
Así que ahora, puedes iterar sobre:
foreach(var a in myModel.AComponents)
{
...
}
foreach(var b in myModel.BComponents)
{
...
}
Además, las listas de componentes IF A y B siempre tienen la misma longitud, podría tener un enumerador sobre una Tuple
de ellos en .NET 4.0 y usar el método Linq Zip ():
public IEnumerable<Tuple<IComponetA, IComponetB>> Components
{
get
{
return ListOfComponentA.Zip(ListOfComponentB, (a,b) => Tuple.Create(a,b));
}
}
Tal vez algo en este sentido funcionaría. hace una clase pública MyCollection (T): IEnumerable (T) para usar el mismo tipo para manejar 2 tipos diferentes de datos
podría ir más lejos con MyCollection (T, X, Y, Z): IEnumerable (T) donde X, Y, Z son listas de sus otros tipos, excepto que la T sería su iterador maestro ejecutándose internamente iterando sus otras listas en paralelo.
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Start.....");
for ( int i = 0; i < 10; i++)
{
IEnumerable collection;
Console.WriteLine();
if (i % 2 == 0)
{ Console.Write("Ints..... ");
collection = new MyCollection<int>(new int[] { 1,2,3 }); }
else
{ Console.Write("Strings..... ");
collection = new MyCollection<string>(new string[] { "x","y","z" } ); }
foreach (var item in collection) { Console.Write(item); }
Console.WriteLine();
}
Console.WriteLine(); Console.WriteLine("End....."); Console.ReadLine();
}
}
public class MyCollection<T> : IEnumerable<T>
{
private T[] _Tdata;
public MyCollection4(T[] arrayOfT)
{
_Tdata = arrayOfT;
}
public IEnumerator<T> GetEnumerator()
{
var listofT = new List<T>(_Tdata);
return listofT.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Un gato no es una secuencia de piernas, ojos y bigotes. Más bien, tiene una secuencia de piernas, ojos y bigotes. Composición sobre herencia .
En lugar de pensar en ComponentModel
como dos secuencias completamente no relacionadas de IComponentA
e IComponentB
, debes eliminar esto y simplemente agregar métodos:
public IEnumerable<IComponentA> GetSequenceOfComponentA() { // }
public IEnumerable<IComponentB> GetSequenceOfComponentB() { // }
(¿Qué esperas que haga foreach(var x in componentModel) { // }
? ¿Qué sucede aquí: public void M(IEnumerable<IComponentA> a) { }
and public void M(IEnumerable<IComponentB> b) { }
and entonces M(componentModel)
?)