Wir haben hier eine 1.1er-GetXYZ-Methode, die 47 Überladungen beinhaltet, fast alle in der Art (filtere auf key1/value1, ..., keyn/valuen). Deutlich zu viele, wie ich meine.
Aufruf wäre bspw.
MyObject m = GetXYZ(iRow, sTableName, sKey1, sValue1, sKey2, sValue2, sKey3, sValue3, sKey4, sValue4, sKey5, sValue5);
Ich möchte diese nun reduzieren und bin auf der Suche nach der intuitivsten Strukturierung davon.
Man könnte es mit einem Dictionary<string, string> als Parameter definieren:
MyObject m = GetXYZ(iRow, sTableName, myDic);
Nachteil hieran wäre, dass ich immer ein myDic initialisieren muss und alle Key-Value-Paare hinzufügen müsste.
Ich könnte auch Extensionmethods für MyObject definieren:
m.Add(key1, value1);
m.Add(key2, value2);
Beim dann folgenden Aufruf von GetXYZ würde ich dann die Key-Value-Paare berücksichtigen. Nachteil hier wäre, dass die Extensions eben immer aufrufbar wären und nicht nur dann, wenn ich GetXYZ aufrufe. Man müsste also immer auf den Anwender vertrauen, dass er das Ding auch wieder cleart (oder das eben immer automatisch am Ende von GetXYZ tun, was auch nicht immer gewollt sein könnte).
Optionale Parameter gingen auch, allerdings empfinde ich die auch irgendwie als unsauber, hinzu kommt, dass die Anzahl der Key-Value-Paare auf n begrenzt ist (analog zum Ist-Zustand).
Tja, wie mache ich das nun? Gibt doch bestimmt noch coole andere Sprachfeatures von C# 4.0 die mir das erleichtern, oder?
Grüße,
Chris
"Wenn Architekten genauso bauen würden, wie Programmierer programmieren, dann würde der erste Specht, der vorbeikommt, die Zivilisation zerstören." (Steven Weinberg)
Hallo,
du kannst nen Sprachfeature verwenden, welches es seit .Net 1.1 gibt: params
Baka wa shinanakya naoranai.
Mein XING Profil.
Und ein Parameter-Objekt gefällt dir nicht?
public class GetXyzParams
{
public int Row { get; set; }
public string TableName{ get; set; }
public string Key1 { get; set; }
public string Value1 { get; set; }
}
public void GetXYZ(GetXyzParams parameters) { ... }
Die kannst du dann auch einfach via Objektinitialisierer aufrufen:
GetXYZ(new GetXyzParams{ Row = 1,TableName = "Customer" });
Oder, wenn du (nur) deine Key-Value-Paare beliebig oft übergeben möchtest:
public void GetXYZ(int row,string tableName,params KeyValuePair[] keyValuePairs) { ... }
Wie wärs mit Fluid Interfaces, ungefähr so
clsDataQuery qry = new clsDataQuery("fa").fa_adnr().IsEqual(10101).And().fa_admatch().IsLike("H%");
// oder so
MyObject m = GetXYZ(iRow, sTableName).AddKey(key1, value1).AddKey(key2, value2);
?
Grüße Bernd
Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3
Hallo,
du kannst nen Sprachfeature verwenden, welches es seit .Net 1.1 gibt: params
Kenn ich, finde ich aber bei Erweiterungen problematisch, da dann keine Überladung hinten angefügt werden kann.
Und ein Parameter-Objekt gefällt dir nicht?
public class GetXyzParams { public int Row { get; set; } public string TableName{ get; set; } public string Key1 { get; set; } public string Value1 { get; set; } } public void GetXYZ(GetXyzParams parameters) { ... }
Die kannst du dann auch einfach via Objektinitialisierer aufrufen:
GetXYZ(new GetXyzParams{ Row = 1,TableName = "Customer" });
Oder, wenn du (nur) deine Key-Value-Paare beliebig oft übergeben möchtest:
public void GetXYZ(int row,string tableName,params KeyValuePair[] keyValuePairs) { ... }
Wie auch oben geschrieben kenne ich es, auch wenn ich an die Auslagerung in eine Klasse nicht nachgedacht habe, allerdings gefällt mir der Aufruf irgendwie auch nicht hundertprozentig:
MyObject m = GetXYZ(iRow, sTable, new KeyValuePair(key1, value1), new KeyValuePair(key2, value2), KeyValuePair(key3, value3), ....);
Wie wärs mit Fluid Interfaces, ungefähr so
clsDataQuery qry = new clsDataQuery("fa").fa_adnr().IsEqual(10101).And().fa_admatch().IsLike("H%"); // oder so MyObject m = GetXYZ(iRow, sTableName).AddKey(key1, value1).AddKey(key2, value2);
?
Grüße Bernd Sowas wie die 2. Lösung würde ich persönlich anstreben, allerdings hapert es bei der Umsetzung ein wenig (AddKey müsste ja jeweils MyObject zurückgeben). Wie ich allerdings grad sehe müsste ich wohl eigentlich immer nur
return this;
in den entsprechenden Methoden zurückgeben, was an und für sich ja auch logisch ist.
Immer schön, wenn man einen Namen zu dem hat, was man sucht 👍
Allerdings müsste ich dann in AddKey irgendwie einschränken an welchen Methoden es aufrufbar ist, ansonsten habe ich ja wieder das gleiche Problem wie bei Extensions.
Grüße,
Chris
"Wenn Architekten genauso bauen würden, wie Programmierer programmieren, dann würde der erste Specht, der vorbeikommt, die Zivilisation zerstören." (Steven Weinberg)
Wie schon talla gesagt hat am einfachsten wäre params:
private void GetXYZ(int row, string tableName, params KeyValue[] keyValuePairs)
{
foreach (var item in keyValuePairs)
{
}
}
public struct KeyValue
{
private string key;
private string value;
}
Ich versteh dein Problem mit überladenungen hinten anfügen gerade nicht,
ja die KeyValuePairs müssen immer am ende stehen das sollte aber doch kein Problem sein oder?
Wie auch oben geschrieben kenne ich es, auch wenn ich an die Auslagerung in eine Klasse nicht nachgedacht habe, allerdings gefällt mir der Aufruf irgendwie auch nicht hundertprozentig.
Vermischt du hier die Auslagerung in eine Klasse und die params-Möglichkeit? Das waren zwei verschiedene, voneinander unabhängige Vorschläge von mir.
Was gefällt die an der Klassenvariante mit Objektinitialisierern nicht?
Vielleicht gefällt dir dann ja sowas:
public class Filter {
List<KeyValuePair<string, string>> _filter;
private int _row;
private string _tableName;
public Filter(int row, string tableName) {
_filter = new List<KeyValuePair<string, string>>();
_row = row;
_tableName = tableName;
}
public Filter Add(string key, string value) {
return Add(new KeyValuePair<string, string>(key, value));
}
public MyObject Get() {
return new MyObject.Get(_row, _tableName, _filter.AsEnumerable());
}
}
public class MyObject {
public static MyObject Get(int row, string tableName, IEnumerable<KeyValuePair<string, string>> filter) {
//...
}
public static Filter GetWithFilter(int row, string tableName) {
return new Filter(row, tableName);
}
}
//benutzen:
MyObject x = MyObject.GetWithFilter(42, "xxx").Add("test", "123").Add("blubb", "bla").Add("foo", "bar").Get();
Darth Maim
Ich würde auch zu einzelnen Add-Operationen tendieren.
Also das da
m.Add(paar 1);
m.Add(paar 2);
Zig Parameter in einem Aufruf übergeben finde ich nicht sehr übersichtlich. Vor allem wenn es key-value Paare sind, wärs für mich anschaulicher wenn die Wertepaare einzeln in einem Aufruf erkennbar wären.
Man kann ja auch ohne Probleme folgendes schreiben:
MyObject x = MyObject.GetWithFilter(42, "xxx")
.Add("test", "123")
.Add("blubb", "bla")
.Add("foo", "bar")
.Get();
Darth Maim
Hallo UNeverNo,
Man müsste also immer auf den Anwender vertrauen, dass er das Ding auch wieder cleart
warum sollte Add eine Methode von MyObject sein? Es geht doch offensichtlich darum, einen Filter zu definieren. Also
MyObject m = GetXYZ (filter);
Wobei der Filter seine eigenen fluent Methoden haben kann. Im Ergebnis
MyObject m = GetXYZ (new Filter (iRow, sTableName).Add (sKey1, sValue1).Add (sKey2, sValue2)... );
Das sorgt dann für klare Trennung und beseitigt das Problem von Darth Maims Vorschlag, dass sich MyObject und Filter gegenseitig kennen müssen. Es reicht, wenn MyObject Filter kennt. Außerdem vermeidet man die Konstruktion mit dem zusätzlichen Get am Ende.
herbivore
Wobei der Filter seine eigenen fluent Methoden haben kann. Im Ergebnis
MyObject m = GetXYZ (new Filter (iRow, sTableName).Add (sKey1, sValue1).Add (sKey2, sValue2)... );
Das sorgt dann für klare Trennung und beseitigt das Problem von Darth Maims Vorschlag, dass sich MyObject und Filter gegenseitig kennen müssen. Es reicht, wenn MyObject Filter kennt. Außerdem vermeidet man die Konstruktion mit dem zusätzlichen Get am Ende.
Hi herbivore,
das Get stört mich persönlich am Ende auch, allerdings bin ich mir nicht sicher bzw. sehe momentan nicht, wie ich Deinen Vorschlag effektiv umsetze. Das iRow und sTableName sind Überladungen der GetXYZ unabhängig vom Filter (die paar würde ich auch belassen). Wenn ich es nun so wie Du vorschlägst umsetze, dann hätte ich wieder eine Überladungsebene mehr, was die Anzahl selbiger verdoppelt. Korrigiere mich, wenn ich es falsch verstehe.
Parallel dazu mal den aktuellen Stand meiner Bemühungen:
public class MyObject
{
private Dictionary<string, object> myDic = new Dictionary<string, object>();
public Dictionary<string, object> Dic
{
get { return myDic; }
set { myDic = value; }
}
}
public static class FluentFactory<T>
{
public static IFluentMethods<T> Create(MyObject myObject)
{
return new Fluent<T>(myObject);
}
}
public class Fluent<T> : IFluentMethods<T>
{
private MyObject _myObject;
public Fluent(MyObject myObject)
{
_myObject = myObject;
}
public IFluentMethods<T> Where(string key, object value)
{
_myObject.Dic.Add(key, value);
return this;
}
public T Get(string key)
{
return (T)Convert.ChangeType(this._myObject.Dic[key], typeof(T));
}
}
public interface IFluentCreate<T>
{
IFluentMethods<T> Create(MyObject myObject);
}
public interface IFluentMethods<T>
{
IFluentMethods<T> Where(string key, object value);
T Get(string key);
}
Hier ärgere ich mich genau mit der Rückgabe herum. In meinem Test hier muss ich schon bei der Factory-Initalisierung einen Datentyp angeben, eigentlich würde ich dies aber nur beim Get machen wollen.
var fluentObject = FluentFactory<string>.Create(myObject).Where("1", "5").Where("2", "7").Where("3", "11");
string s = fluentObject.Get("2");
Vielleicht nochmal ein wenig ausführlicher, was ich hier machen will. Ich habe ein altes 1.1-Objekt und will einfacher und intuitiver darauf zugreifen, fluent interfaces scheinen mir dafür eine schöne Umsetzung zu sein. Ich will dieses Objekt nun wrappen und die in ihm enthaltenen Unterobjekte in verschiedene Datentypen (string, bool, int, double) casten.
Grüße,
Chris
"Wenn Architekten genauso bauen würden, wie Programmierer programmieren, dann würde der erste Specht, der vorbeikommt, die Zivilisation zerstören." (Steven Weinberg)
Hallo UNeverNo,
ich sehe dein Problem nicht und mir scheint du treibst da einen unnötigen Riesenaufwand. Wenn du iRow und sTableName als Parameter von GetXYZ belassen willst bitte, dann eben:
MyObject m = GetXYZ (iRow, sTableName, new Filter ().Add (sKey1, sValue1).Add (sKey2, sValue2)... );
Wenn du den bestehenden Code minimal-invasiv ändern willst, braucht du keinen Wrapper, keine Factory, keine Interfaces, nichts mit Fluent im Namen, sondern wirklich nur eine Klasse Filter, mit einen leeren Konstruktor und einer (fluent) Add-Methode. Mehr nicht (ok, noch eine Methode o.ä. zum Auslesen der Key-Value-Werte). Und das solltest du alleine umgesetzt bekommen.
herbivore
Was mir an dem new Filter einfach nicht so gefällt ist eine Verdopplung der Überladungen, wie ich es oben schon mal versuchte zu beschreiben.
Habe ich standardmäßig
GetXYZ()
GetXYZ(iRow)
GetXYZ(iRow, sTable)
hätte ich nun
GetXYZ()
GetXYZ(oFilter)
GetXYZ(iRow)
GetXYZ(iRow, oFilter)
GetXYZ(iRow, sTable)
GetXYZ(iRow, sTable, oFilter)
Und das würde ich - wenn möglich - verhindern. Oder habe ich mich gerade gedanklich verrannt? Dass ich das selbst umsetzen kann ist klar und das will ich auch 😉
Alternativ könnte man es wohl auch mit optionalen Parametern machen, aber empfindet ihr/empfindest du das als eine saubere Alternative?
Grüße,
Chris
"Wenn Architekten genauso bauen würden, wie Programmierer programmieren, dann würde der erste Specht, der vorbeikommt, die Zivilisation zerstören." (Steven Weinberg)
Hallo UNeverNo,
nein, du hast eine Reduzierung der Überladungen, denn statt
GetXYZ(...)
GetXYZ(..., key1, value1)
GetXYZ(..., key1, value1, key2, value2)
GetXYZ(..., key1, value1, key2, value2, key3, value3)
usw.
brauchst du nur noch
GetXYZ(..., oFilter)
Wenn der Filter nicht nötig ist, kannst du einfach einen leeren Filter übergeben, also
GetXYZ(..., new Filter ())
das heißt, die Überladung
GetXYZ(...)
brauchst du nur aus Komfortgründen, um dir die Erzeugung des leeren Filters sparen zu können. Aber selbst dann hast du pro Grundüberladung (damit meine ich die drei Gruppen, je nachdem ob man für das "..." innerhalb der Klammern leer, "iRow", oder "iRow, sTable" einsetzt) nur zwei statt n Überladungen.
Nach meinem Geschmack sind wir jetzt wirklich bei [Hinweis] Wie poste ich richtig? Punkt 1.1.1 angekommen. Sollte ich etwas wesentliches übersehen, schreib eine PM.
herbivore