Laden...

Delegate für enum return value

Erstellt von rollerfreak2 vor 13 Jahren Letzter Beitrag vor 13 Jahren 3.671 Views
rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 13 Jahren
Delegate für enum return value

Hallo zusammen,

ich habe grad ein Problem und seh den Wald vor lauter Bäumen wahrscheinlich nicht. Ich habe eine Dictionary mit Key=string und Value=delegate von folgenden Typ.


private Dictionary<string, GetProperty> m_dictionaryProperty;

public delegate object GetProperty();

Nun will ich da eine Methode hinzufügen die jedoch einen typen zurück gibt der nicht null sein darf, z.b. DateTime oder ein enum. Unter anderem aber auch methoden die null able type zurück geben. Mir ist klar warum das delegate nicht passt, mir ist aber nicht klar wie ich das delegate definieren kann damit ich beide methoden typen rein packen kann.

Kann mir einer von euch auf die Sprünge helfen?

Again what learned...

5.657 Beiträge seit 2006
vor 13 Jahren

Vielleicht so:


private Dictionary<string, Func<DateTime?>> m_dictionaryProperty;

private DateTime? TestMethod()
{
  return null;
}

//....
m_dictionaryProperty.Add("test", TestMethod);


Weeks of programming can save you hours of planning

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 13 Jahren

Dann geht das doch aber nicht...


private Dictionary<string, Func<DateTime?>> m_dictionaryProperty;

private DateTime? TestMethod1()
{
  return null;
}

private myDataType TestMethod2()
{
  return ...;
}

private PublicEnum TestMethod3()
{
  return ...;
}


//....
m_dictionaryProperty.Add("test1", TestMethod1); 
m_dictionaryProperty.Add("test2", TestMethod2); 
m_dictionaryProperty.Add("test3", TestMethod3); 

Again what learned...

795 Beiträge seit 2006
vor 13 Jahren

Wenn du sowas machen willst, musst du DateTime?, myDataType und PublicEnum auf den größtmoglichen gemeinsamen Nenner bringen. Und das ist in diesem Fall nunmal Object. Also wirst du um das casten nicht herumkommen.

Gruß, Christian.

`There are 10 types of people in the world: Those, who think they understand the binary system Those who don't even have heard about it And those who understand "Every base is base 10"`
656 Beiträge seit 2008
vor 13 Jahren

Wenn du sowas machen willst, musst du DateTime?, myDataType und PublicEnum auf den größtmoglichen gemeinsamen Nenner bringen. Und das ist in diesem Fall nunmal Object. Also wirst du um das casten nicht herumkommen.

Gruß, Christian.

...nur ist das vor .NET 4.0 nicht möglich (Stichwort Co-/Contra-Variance).

Wobei das hier irgendwie so aussieht, als ob du irgendwas machen willst, was in der Art nicht wirklich Sinn macht.
Wo verwendest du die Delegates? So, wies jetzt ist, kann die entsprechende Stelle gar nicht wissen, ob es sich um DateTime, myDataType oder PublicEnum handelt. In dem Fall kannst du aber auch gleich Dictionary<string, Delegate> verwenden und Delegate.Invoke/DynamicInvoke benutzen.

1.361 Beiträge seit 2007
vor 13 Jahren

...nur ist das vor .NET 4.0 nicht möglich (Stichwort Co-/Contra-Variance).

Nicht Covariance and Contravariance in Generics mit Covariance and Contravariance in Delegates verwechseln. Letztere gibt es seit es Delegates gibt, und damit schon seit .NET 2.0.

beste Grüße
zommi

656 Beiträge seit 2008
vor 13 Jahren

Nicht
>
mit
>
verwechseln. Letztere gibt es seit es Delegates gibt, und damit schon seit .NET 2.0.

Und was ist deiner Meinung nach Func<object>? Oder meintest du (bzw TheBrainiac) stattdessen Dictionary<string, object>?

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 13 Jahren

Die auswertende Method nutzt TypeConverter um das auszuwerten bzw. darzustellen. Da die Methoden am Interface definiert sind, kann ich die nicht so einfach ändern. Ich habs jetzt mit Delegate gelöst und das via DynamicInvoke aufgerufen. So muss ich zwar mehrere delegaten definieren, aber ich die methoden aufrufe bleiben generisch!

Again what learned...

1.361 Beiträge seit 2007
vor 13 Jahren

Hi,

@rollerfreak2
Ich würde diese ganze Konvertierungslogik in eine extra Klasse auslagern.

        
class MethodDictionary : Dictionary<string, Func<object>>
{
  public void Add<T>(string s, Func<T> p)
  {
    base.Add(s, delegate() { return p(); });
  }
}

Intern speichert er andere Delegaten ab, die intern die eigentliche Funktion erst aufrufen un den Rückgabewert gegebenenfalls in ein Object casten/boxen.
Du kannst im Übrigen auch gerne Composition anstatt Inheritance nehmen, um das Dictionary zu erweitern

Mit dieser Klasse kannst du nun deine Methoden einfach hinzufügen:

            
m_dictionaryProperty.Add<DateTime?>("test1", TestMethod1);
m_dictionaryProperty.Add<myDataType>("test2", TestMethod2);
m_dictionaryProperty.Add<PublicEnum>("test3", TestMethod3);

Und brauchst für einen neuen Rückgabetyp/eine neue Methodenart keinen neuen Delegaten zu definieren.
Ab dem .NET 4.0 Compiler kannst du auch die Typen-Angabe in den Spitzen klammern weglassen, aber alle vorherigen Compiler bekommen das mit der Type-Inference nicht ausreichend gut hin

Schön finde ich auch, dass du die Typ-Konvertierung, die du beim Aufrufen noch durchführen möchtest, auch in die anonyme Methode in der Add<T>-Funktion gleich integrieren kannst.
Also ich weiß jetzt ja nicht, worein später alles konvertiert wird. DateTime vielleicht?
Dann kannste natürlich auch gleich folgendes verwenden.


class MethodDictionary : Dictionary<string, Func<DateTime>>
{
  public void Add<T>(string s, Func<T> p)
  {
    base.Add(s, delegate() 
      { 
        // Funktion aufrufen
        T value = p(); 

        DateTime result;
        // Konvertierung durchführen
        // ...
        return result;
      }
      );
  }
}

So wirfst du in das Dictionary die beliebigen Methoden rein, aber wenn du mit den Methoden im Dictionary dann arbeitest, also mit dem []-Operator drauf zugreifst und aufrufen willst, sind sie schon genau richtig typisiert und konvertiert.

@BhaaL

Und was ist deiner Meinung nach Func<object>?

Ich meinte, dass es genügen sollte einen einzigen Delegaten mit Object als return-Type zu definieren. Die Delegaten-Kovarianz übernimmt den Rest. Dachte ich! Allerdings klappt das nur bei Referenztypen. Ein Werttyp (DateTime, DateTime?, Enumeration, ...) lässt sich jedoch nicht kovariant für Object einsetzen.
(Auch ab .NET 4.0 geht sowas nicht, wie "IEnumerable<object> x = new List<int>()", wohingegen es mit string oder allen anderen Referenztypen klappt)

Und auch wenn Func<T> ja selbst ein Generic ist, ergibt sich die Möglichkeit einer Func<object>-Variable auch eine Methode zuzuweisen, die string zurück gibt, eben aus der Kovarianz der Delegaten (seit .Net 2.0) und nicht die Kovarianz der Generics (seit .Net 4.0)


static string foo() { return "foo"; }

Func<object> a = foo;
Func<object> b = new Func<string>(foo);

Beide zeilen nutzen Kovarianz, da foo einen string und kein (reines) object zurückgibt.
Die erste Zeile funktioniert einwandfrei mit .NET 2.0, da sie auf der DelegatenKovarianz beruht.
In der zweiten Zeile wird explizit die generische Kovarianz erfordert, weshalb diese Zeile erst ab .NET 4.0 funktionieren kann.

(wer das testen will, muss sich entweder den Func<T>-Delageten selbst als "delegate T Func<T>()" definieren, oder verwendet .NET 3.5, dort gibts den schon, aber noch keine generische Kovarianz)

beste Grüße
zommi

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 13 Jahren

Hi zommi,

das mit dem Func<T> ist ne gute Idee, geht leider nicht weil das m.E. erst ab .NET 3.0 geht, und wir sind hier ersteinmal auf 2.0 beschränkt.
Die convertion ist ne andere geschichte, da gibt es einen globalen life cycle, daher die entsprechende Komponente wir gefragt ob sie in einen entsprechenden DatenTyp wandeln kann, wenn ja wird sie gebeten einen converter zur Verfügung zu stellen. Das dictionary brauch ich nur um diese Komponente bisschen generisch zu halten.

Again what learned...

656 Beiträge seit 2008
vor 13 Jahren

Und auch wenn Func<T> ja selbst ein Generic ist, ergibt sich die Möglichkeit einer Func<object>-Variable auch eine Methode zuzuweisen, die string zurück gibt, eben aus der Kovarianz der Delegaten (seit .Net 2.0) und nicht die Kovarianz der Generics (seit .Net 4.0)

  
static string foo() { return "foo"; }  
  
Func<object> a = foo;  
Func<object> b = new Func<string>(foo);  
  

Beide zeilen nutzen Kovarianz, da foo einen string und kein (reines) object zurückgibt.
Die erste Zeile funktioniert einwandfrei mit .NET 2.0, da sie auf der DelegatenKovarianz beruht.
In der zweiten Zeile wird explizit die generische Kovarianz erfordert, weshalb diese Zeile erst ab .NET 4.0 funktionieren kann.

Wieder was gelernt...allerdings benutze ich kaum noch Methoden, wenn ichs in einer Lambda-Expression < 3 Zeilen lösen kann - drum hab ich die Möglichkeit vermutlich verdrängt:

Func<string> s = () => "foo";
Func<object> so = s; //geht natürlich nicht

das mit dem Func<T> ist ne gute Idee, geht leider nicht weil das m.E. erst ab .NET 3.0 geht, und wir sind hier ersteinmal auf 2.0 beschränkt.

2.0 kennt Generics und Delegates, was hält dich davon ab, dein eigenes Func<T> zu definieren? zommi hat da schon den entsprechenden Hinweis gegeben.

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 13 Jahren

2.0 kennt Generics und Delegates, was hält dich davon ab, dein eigenes Func<T> zu definieren?

Ich wüsste nicht wie ich das definieren soll!? Was ist Func für ein Typ? Ist das ein generisches delegate mit einem dynamischen Rückgabetyp?
Und wenn das so geht, dann gleich die nächste Frage, wieso kann ich dann dort not null-able typen rein stecken, und in ein statisches delegate vom typ rückgabetyp object nicht?

Again what learned...

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo rollerfreak2,

Ich wüsste nicht wie ich das definieren soll!? Was ist Func für ein Typ?

bitte sowas immer selbst in der :rtfm: Doku nachschauen. Selbst wenn du nur die .NET 2.0-Doku installiert hast, online findet man auch für für neuere Framework-Versionen alles.

Und wenn das so geht, dann gleich die nächste Frage, wieso kann ich dann dort not null-able typen rein stecken, und in ein statisches delegate vom typ rückgabetyp object nicht?

Das beantwortet sich automatisch, wenn du nachgeschaut hast.

herbivore

1.361 Beiträge seit 2007
vor 13 Jahren

Hi rollerfreak2,

Im letzten Satz meines letzten Posts steht auch, wie Func<T> definiert/definierbar ist. Aber auch wie herbivore dir geraten hat, immer erst in die Doku schaun. In der msdn findest du schließlich die Definition zu Func<T>.

Und wenn das so geht, dann gleich die nächste Frage, wieso kann ich dann dort not null-able typen rein stecken, und in ein statisches delegate vom typ rückgabetyp object nicht?

Das habe ich auch versucht oben zu erläutern.
ValueTypes und ReferenceTypes sind grundverschieden. Zwar sind alle ValueTypes wie Enums, Structs, Nullables (das sind auch structs, schau in die Definition von Nullable<T>) irgendwie doch von Object abgeleitet. Aber nur so irgendwie. Man kann sie nicht ohne Weiteres als ein Object verwenden.
Dafür gibts den "Workaround" des Boxing/Unboxing. Der Compiler übernimmt das Boxing/Unboxing zwar an vielen Stellen, aber bei Kovarianz/Kontravarianz wird kein Boxing getätigt.
Deshalb kannst du, wenn ein Object und damit ein ReferenceType erwartet wird, keinen ValueType zurückgeben.

Und warum das bei den Generics funktioniert? Naja, ließ dir mal durch, wie in .NET Generics umgesetzt sind. Das sollte dann klar werden, wenn du da mal nachgelesen, bzw. in der Doku nachgeschaut hast.

beste Grüße
zommi

rollerfreak2 Themenstarter:in
916 Beiträge seit 2008
vor 13 Jahren

Okay habs jetzt kappiert warum es mit Generics funktionieren würde. Nichts desto trotz kann ich kein


Func<bool> generics = delegate() { return true; };

definieren. Weil ich wie gesagt auf .NET 2.0 beschränkt bin. Und wenn ich das dann mit einem .NET 2.0 Compiler kompiliere, dann kommt die Meldung:

The type or namespace name 'Func' could not be found (are you missing a using directive or an assembly refernece?)

Ich kann es vielleicht mit 2.0 nutzen, aber nicht mit 2.0 übersetzen, oder irre ich mich?

@herbovire

bitte sowas immer selbst in der Doku nachschauen. Selbst wenn du nur die .NET 2.0-Doku installiert hast, online findet man auch für für neuere Framework-Versionen alles.

hast recht, da hätte ich vorher rein schauen sollen.

Again what learned...

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo rollerfreak2,

The type or namespace name 'Func' could not be found (are you missing a using directive or an assembly refernece?)

wie jetzt schon mehrfach gesagt, kannst du dir einen entsprechenden Delegattyp leicht selber definieren. Du kannst ihn sogar genauso nennen.

herbivore