Anzeige

foreach - Eine Meinung


jccTeq

Erfahrenes Mitglied
#1
Hallo Leute,

ich stolpere immer wieder darüber, daß man mit foreach keine selektive Auswahl in einer Collection treffen kann. Für mich ist foreach mangelhaft umgesetzt. Es wäre doch die einmalige Gelegenheit gewesen, den C# Entwicklern ein Konstrukt zu geben, welches über eine Collection eine Art SQL-, also Datenbank-Funktionalität abbildet. Worauf ich hinaus will, das ist folgendes:

ein normales foreach-Konsturkt sieht ja so aus:
Code:
foreach(Object bla in xxx)
{
...
}
in SQL sähe das ja so aus:
Code:
bla = sqlquery("select * from xxx");
jetzt wäre es schön gewesen, wenn man in foreach noch hätte selektieren können, welche Objekte aus xxx man benötigt, also wie in SQL:
Code:
select * from xxx WHERE blubb='bli';
was mit foreach so ähnlich hätte realisiert werden können:
Code:
foreach(bla in xxx where blubb=="bli")
... was eben in bla nur objekte geliefert hätte, deren Eigenschaft blubb den Wert "bli" enthält.

In der tatsächlichen Syntax der foreach muss man in jedem Schleifendurchlauf erstmal abfragen, ob blubb wirklich den Wert "bli" enthält:

Code:
foreach(bla in xxx)
{
  if(bla.blubb != "bli") continue;
}
Ich find's schade, daß es sowas nicht gibt... hätte mir schon 'ne Menge zusätzlicher Arbeit erspart.

Das ganze hätte man bestimmt auch noch weiter spinnen können, in Richtung SQL-Funktionalität. LIMITs und so... logische Verknüpfungen (WHERE blubb='bli' AND bloek='muh' oder so)...

Das ist meine Meinung! Was meint ihr dazu?
 
#2
Hi.

foreach mit einer Select-Anweisung aus SQL zu vergleichen passt, finde ich zumindest, nicht so ganz.
Mit dem SQL-Select holt man sich Datensätze gewisse Datensätze die gewisse Kriterien erfüllen.
Das foreach iteriert einfach durch eine Collection durch und du kannst dann mit jedem Objekt darin etwas machen. Eine Abfrage kannst dann im Anweisungsblock noch immer machen und ein
Code:
if (!bla.Blubb.Equals("blu"))
  continue;
ist ja nicht soviel Aufwand.

Machbar wäre aber ein von dir gewünschtes Konstrukt sicher. Wenn du es oft brauchst kannst es dir im Prinzip auch selbst schreiben. Nur schauts dann halt ein bisserl anders aus.
Es wird dann ein Funktionsaufruf sein dem du verschiedene Parameter übergibst, unter anderem auch einen Delegate zu einer Funktion die dann mit den gefundenen Objekten ausgeführt werden soll.

So könnte das aussehen:

Code:
/*
 *
 * A simple example how to extend the foreach-construct
 *
 * (c) 2004 Alexander Schuc
 * free for use and distribution
 *
 */

using System;
using System.Reflection;

namespace furred.Testing
{
  public class TestObject
  {
    public string Name;
    public int Number;
    
    public TestObject(string name, int number)
    {
      this.Name = name;
      this.Number = number;
    }
  }

  public delegate void mydelegate(TestObject obj);
  
  public class advancedForeachTesting
  {
    public static void Main(string[] args)
    {
      new  advancedForeachTesting();
    }

    private mydelegate executeStatement;
    
    public advancedForeachTesting()
    {
      TestObject[] to = new TestObject[20];
      for (int i = 0; i < 20; i++)
      {
        string name = i % 2 == 0 ? "Blaa" : "Blubb";
        to[i] = new TestObject(name, i);
      }

      object[] objs = new object[20];
      for (int i = 0; i < 20; i++)
      {
        int mod = i % 3;
        switch (mod)
        {
          case 0: objs[i] = (i+20); break;
          case 1: string name = i % 2 == 0 ? "Muh" : "Kuh";
                  objs[i] = new TestObject(name, i+20); break;
          case 2: objs[i] = "Blaa"; break;
        }
      }

      executeStatement  = new mydelegate(printInfos);
      Console.WriteLine("Testing with TestObject-Array:");
      aForEach("furred.Testing.TestObject", to, "Name", "Blaa", executeStatement);

      Console.WriteLine("Testing with object-Array:");
      aForEach("furred.Testing.TestObject", objs, "Name", "Kuh", executeStatement);
    }

    private void printInfos(TestObject obj)
    {
      Console.WriteLine("#{0:00} - {1}", obj.Number, obj.Name);
    }

    private void aForEach(string type_name, object[] collection, string prop, string equal, mydelegate fun)
    {     
      Type type = Type.GetType(type_name);
      
      foreach (object o in collection)
      {      
        if (o.GetType() == type)
        {
          string p = (string) type.InvokeMember(prop,BindingFlags.GetField, null, o, null);
          if (p == equal)
            fun((TestObject)o);
        }
      }
    }
  }
}
MfG,
Alex
 

Anhänge

Christian Fein

Erfahrenes Mitglied
#3
foreach iteriert durch eine Collection nicht mehr und nicht weniger.

Was du haben willst ist demnach ein eigener Algorythmus Objecte anhand
eines Filters zu "finden".

Hier den Filter ein die Iteration einzubauen ist nicht wünscheswert. Denn
letztendlich müsste er in alle Klassen die eine Collection darstellen und
per foreach durchlaufen werden wollen unnötige Methoden bereitstellen.

Wenn du Collections haben willst die du aufgrund bestimmter Keys
bekommen willst dann ist es das richtige diese Collections in eine
Hashtable mit dem gewünschten Key zu packen.
 

XChris

Erfahrenes Mitglied
#4
Meine Meinung:

Ne foreach ist zum DURCHLAUFEN von Container da. Den Selectiven Zugriff mit EINER Zugriffsmethode effizient erschlagen zu wollen ist unmöglich.

Du musst selbst entscheiden, was Du für einen Conatiner willst und brauchst. Das was Du Dir wünschst, nehmlich den Zugriff auf ein Element via Eigenschaft gibt es, Schau Dir mal - wie Christian Fein schon sagte, den Namespace Collection mal an. Wenn Du mal selbst eine verkettet Liste oder gewichteten Baum programmiert hast, weisst Du was ich meine.

Chris
 

Sunray

Erfahrenes Mitglied
#5
Eine gute Idee wäre, selber eine Collection oder Dictionary (wegen den Primary Keys) Klasse zu schreiben, die über Reflection dann gewisse Ausdrücke auswerten kann.
VB.NET:
Code:
For Each resObj as Object in IntelligentCollection.Limit( _ 
   "param1 < 1900 and param2 = 'weihnachten' limit 3 sort by param1 asc")
	'//Code
Next
Im .NET Framework 2.0 könnte man das ganze sogar mit Generics machen...
Wirklich interessante Idee...
 

jccTeq

Erfahrenes Mitglied
#7
@Schuc: Check deine Seite mal! Irgendwie scheint das ZIP-File defekt zu sein. Winzip sagt mir immer was von "Bad Offset". Und die Seite hinter [mehr] geht auch nicht.

Das mit der Collection, die eine Limit-Funktion liefert, welche SQL versteht, ist schonmal gar keine schlechte Idee. Trotzdem hätte ich es schön gefunden, wenn foreach das von Haus aus angeboten hätte.
 

Norbert Eder

Erfahrenes Mitglied
#8
Es macht keinen Sinn das foreach für etwas zu missbrauchen, wofür es nicht gedacht war. Warum auch? Damit sollen Collections durchiteriert werden und sonst nichts. Überprüfungen können innerhalb durchgeführt werden.

Also warum hätte man foreach aufblasen sollen? Ich sehe hier wirklich keinen Grund dafür. Da macht es doch mehr Sinn, eine entsprechende Datenstruktur zu basteln oder was auch immer die das für mich übernimmt.
 

jccTeq

Erfahrenes Mitglied
#9
Darum geht es mir ja. Um die Ursache dessen: darum, daß foreach für etwas anderes hätte vorgesehen werden können. Ums Prinzip. Man hätte ihm optional noch die Möglichkeit der Einschränkung geben können und so 'ne Menge Programmieraufwand einsparen können. Das ist es, was ich mit meiner Meinung aussagen wollte.
 

Sunray

Erfahrenes Mitglied
#11
For Each muss wirklich nicht zwangsläufig aufs "normale" Iterieren beschränkt sein.
In VB.NET könnte man das selektive For-Each ruhig ungefähr so implementieren.
Code:
For Each entry As String In myArrayList Where CStr(entry).Length > 2
Für C# müsste man sich dann halt was ausdenken. Aber es ist auf jeden Fall sinnvoller diese Funktion an das bestehende Foreach anzuhängen, weil der grossteil der "Arbeit" ja immer noch das iterieren ist. Ansonsten hättest du 2 Konstrukte, die fast das selbe machen.

Ja, Foreach ist fürs iterieren gedacht. Aber wieso sollte man da nicht mit einem einzigen, simplen Ausdruck die Menge der übergebenen Elemente reduzieren können.
Ich denke, dass auf Seiten des Frameworks (Interfaces, Collection Klassen) nichts geändert werden müsste.
 

Christian Fein

Erfahrenes Mitglied
#12
Hab da ein "Selectiven Iterator" eben in Java mit Hilfe von Generics geschrieben.

Sprich ein Typsicherer selektiver Iterator.
Hauptanwendung ist die übergabe eines Objectes das ISelector<T> implementiert.
Dieser sagt ob jenes object selectiert wird oder nicht.

Der Code (erst das Interface, dann der Iterator, danach die main Methode und Anwendung):

Code:
package de.jorona.test;

public interface ISelectort<T> {
	public boolean select(T object);
}
Implementationen dieses Interface entscheiden ob ein Object in der Iteration berücksichtigt wird oder nicht

Code:
package de.jorona.test;

import java.util.ArrayList;
import java.util.Iterator;

public class SelectIterator<T> implements Iterator {
	
	private ArrayList<T> items = new ArrayList<T>();
	private ArrayList<T> okitems = new ArrayList<T>();
	private ISelectort<T> selectable;
	private int index = 0;
	private Iterator<T> iter;
	
	public SelectIterator(ArrayList<T> items,ISelectort<T> selectable) {
		this.selectable = selectable;
		this.items = items;
		Iterator<T> iterator = items.iterator();
		while(iterator.hasNext()) {
			T item = iterator.next();
			if(selectable.select(item))
				okitems.add(item);
		}
		iter = okitems.iterator();
	}
	
	public boolean hasNext() {	return iter.hasNext(); }
	
 	public T next() { return iter.next();	}
 	
 	public void remove() {	iter.remove();	}			
}
Generische Iterator. Checked anhand übergebener ISelector<T> Implementierung ob
das Object Typs T berücksichtigt wird oder nicht. Wenn ja packt der das ganze in ein neues Array.

Die Anwendung, hier wird nur über Strings iteriert die mit "e" anfangen. Mann kann diese ganze Geschichte natürlich allein durch die übergabe einer anderen ISelector<T> Implementierung ändern
Code:
/*
 * Created on 05.01.2005
  */
package de.jorona.test;

import java.util.ArrayList; 
public class ForEachTest {
	
	public static void main(String[] args) {
		ArrayList<String> items = new ArrayList<String>();
		items.add("eins");
		items.add("zwei");
		items.add("esel");
		items.add("efeu");
		SelectIterator<String> selectIterator = new SelectIterator<String>(items,
				new ISelectort<String>() {
					public boolean select(String arg0) {						
						return arg0.startsWith("e");
					}			
				}
		);
		while(selectIterator.hasNext()) {
			System.out.println(selectIterator.next());
		}
	}
}
In C# würde ich ähnlich machen, ausser natürlich das noch keine Generics existieren, wird Zeit das dies umgesetzt wird.

grüsse
 

Norbert Eder

Erfahrenes Mitglied
#18
.NET 2.0 wird vermutlich noch ein bisserle dauern. Einen Termin wüßte ich ehrlich gesagt nicht ... genausowenig wie ich wüßte, WO .NET noch immer hinter Java wär .... ;)
 
#20
Hi.

Melde mich nochmal zu dem Thema.

Bei Comega, einer Programiersprache von Microsoft zu Forschungszwecken, gibt es einerseits einen neuen Streamtyp dere ien Art speziells Array darstellt (um es kurz zu sagen), und bei diesem ist es möglich, durch alle Elemente zu iterieren, allerdings mit Filtereinschränkungen.

Mehr dazu gibts im dot.net Magazin 3.05.

Hier noch ein Beispiel:

Code:
public int* RandomGenerator() // das ist dieser neue Stream-Typ
{ // das yield bewirkt, das beim Aufruf eine Zahl zurückgegeben wird, und es
  // wird erst weitergearbeitet beim nächsten Aufruf.
  for (int i = 0; i <= 10; i++)
    yield return i;
}

// Das gibt nun alle Zahlen aus:
RandomGenerator().{ Console.WriteLine(it); }

// Dies nur noch Zahlen bei denen bei der Division durch 2 der Rest 0 beträgt
RandomGenerator()[it % 2 == 0].{ Console.WriteLine(it); }
Interessante Sprache finde ich, und sie kann noch viel mehr. Wie gesagt eine Forschungssprache, und gibt keine Pläne eine kommerzielle Version davon auf den Markt zu bringen.
Der Compiler soll sich dennoch relativ gut benutzen lassen, und schöne .net Assemblies erzeugen.

Ich hoffe einige Features der Sprache werden wir bei C# 3.0 sehen. :D

http://research.microsoft.com/Comega

Bei Generic z.b nur um mal ein Beispiel zu nennen
Hab jetzt nur mal den Artikel über Generics im dot.net Magazin gelesen. Dort ist auch ein Vergleich zu den Java Generics. Die Umsetzung in .net scheint mir aber die bessere zu sein. :)

MfG,
Alex
 
Anzeige
Anzeige