Laden...

Lösung für: Anonymen Typ in ein Interface casten

Erstellt von dr4g0n76 vor 14 Jahren Letzter Beitrag vor 7 Jahren 4.989 Views
dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren
Lösung für: Anonymen Typ in ein Interface casten

EDIT: "umwandeln" in Überschrift und Text in "casten" geändert

in C# 3.0 ist es möglich anonyme Typen zu erstellen. Wer LINQ, var & Co. benutzt, kennt das womöglich alles schon.

Heute hat mich aber interessiert, kann man einen anonymen Typ in ein Interface casten? Es ging mir nicht darum, ob es grundsätzlich Sinn macht, sondern nur ob es tatsächlich möglich ist.

Ich hatte damals eine Lösung gepostet, um einem Objekt zur Laufzeit ein definiertes Interface hinzuzufügen:
Interface zur Laufzeit zu einer Klasse hinzufügen (Lösung)

Und mit dieser Kombination ist es tatsächlich möglich:


     var blub = new { Test = "hallo", DoIt = "" };

    public interface IBlub    
    {
        string Test { get; }
        string DoIt { get; }
    }


Probieren wir es aus:


            var blub = new { Test = "hallo", DoIt = "2" };

            IBlub iblub = Latent<IBlub>.Cast(blub);
            MessageBox.Show(iblub.Test);
            MessageBox.Show(iblub.DoIt);


Und tatsächlich, nacheinander wird

"hallo"

und

"2" ausgegeben.

Es geht also. 😃

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren

Ergänzung:

Enthält z.B. DoIt nichts (String.Empty) erhalten wir eine:

Method-Access-Exception:
<>f__AnonymousType0`2.get_Test()

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

6.862 Beiträge seit 2003
vor 14 Jahren

Hallo,

anonyme Typen sind ja nen Kompilerfeature, die Runtime kennt die ja gar nicht. Von daher hätte es mich gewundert wenn es nicht ginge 😃, da anonyme Typen für die Runtime ja ganz normale Typen sind.

Baka wa shinanakya naoranai.

Mein XING Profil.

2.891 Beiträge seit 2004
vor 14 Jahren

anonyme Typen sind ja nen Kompilerfeature, die Runtime kennt die ja gar nicht. Von daher hätte es mich gewundert wenn es nicht ginge 🙂, da anonyme Typen für die Runtime ja ganz normale Typen sind.

Also mich wundert es schon sehr EDIT: naja, eigentlich nicht...

Denn Folgendes geht ja nicht:


class Bar
{
   public string Foo { get; set;}
}

interface IBlub
{
   string Foo { get; set;}
}
...
IBlub blub = new Bar();

EDIT: Hm, das Latent-Dings fügt dem Typen noch zur Laufzeit ein Interface hinzu. (Ja herbivore, ich sollte gleich genauer lesen...

Gruß,
dN!3L

5.299 Beiträge seit 2008
vor 14 Jahren

EDIT: Hm, das Latent-Dings fügt dem Typen noch zur Laufzeit ein Interface hinzu.

ist das überhaupt richtig?

Mir sieht das Latent-Dings wie etwas aus, was mit Reflection auf Klassenmember zugreift, aber das ist doch kein Interface.

Ich versteh Interface als eine zur Designzeit geschlossene Abmachung: "Wer das hier implementiert, der stellt die und die Member zur Verfügung."

Das Latent-Dings scheint mir einfach zu behaupten: der und der implementiert das und das, und wenns stimmt, funzt das auch.

Das ist aber eher, was ich unter casten verstehe.

Der frühe Apfel fängt den Wurm.

4.207 Beiträge seit 2003
vor 14 Jahren

Für mich klingt das nach Duck Typing: Wenn es aussieht wie eine Ente, quakt wie eine Ente und watschelt wie eine Ente - dann ist es eine Ente.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren

@Golo: InteressanteAnsicht,

aber wie ich oben schon gesagt hatte, ob das in diesem Sinne alles zusammen passt, war vorerst egal. Ich wollte nur wissen, ob es mit dieser Methodik geht.

Aber euere Gedanken dazu gefallen mir, finde ich sehr interessant.

Wer möchte kann das ja ohne den Cast mit Latent mal versuchen.

Dann müsste IMHO eine Exception kommen.

Ducktyping musste ich glatt nochmal nachschlagen.

[...]
Duck-Typing ist ein Konzept der objektorientierten Programmierung, bei dem der Typ eines Objektes nicht durch seine Klasse beschrieben wird, sondern durch das Vorhandensein bestimmter Methoden.
[...]
[...]
Dies führt wie bei allen dynamischen Typsystemen zu einer erhöhten Flexibilität, reduziert aber ebenso die Möglichkeit, statisch zur Übersetzungszeit Fehler im Programm zu finden.
[...]

Natürlich ist es aufjeden Fall ein Kompromiss...

Überlegen wir doch mal was wir damit für einen Gewinn/Verlust erzielen, dann kommen wir sicher darauf ob das Konstrukt sinnvoll oder einfach nur Quatsch.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

5.299 Beiträge seit 2008
vor 14 Jahren

Ich find die Latent-Idee echt pfiffig. Da kann man solche gräuslichkeiten


obj.Gettype().GetProperty("Name").SetValue(obj, "Hans");

wegkapseln, und ordentlich typisiert gegen ein adäquates Interface proggen.

Mit anonymen Typen haltichs für Spielerei, aber die typen mag ich eh nicht, weil man sie nicht an andere Methoden weitergeben kann.
Und ehe ich da ein Interface gebastelt und ein Latent drauf angesetzt habe, habich auch eine ordentliche Klasse hingeschrieben.

Der frühe Apfel fängt den Wurm.

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 14 Jahren

Mit anonymen Typen haltichs für Spielerei, aber die typen mag ich eh nicht, weil man sie nicht an andere Methoden weitergeben kann.

Das ist so nicht richtig, siehe angehängtes Code-Beispiel.


      var blub = new { Item = "Hallo" };
            ProcessAnonymousType(blub);
        }


        public void ProcessAnonymousType(object _var)
        {
            var blub2 = _var  //jetzt ist das Item von oben nicht mehr direkt sichtbar
        }

Wenn Du aber meinst, dass man vorerst jetzt nichts mehr mit dem Objekt der Klasse anfangen kann, weil die Items nicht mehr direkt sichtbar sind, stimme ich Dir zu.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

2.891 Beiträge seit 2004
vor 14 Jahren

Hallo dr4g0n76,

EDIT: Hm, das Latent-Dings fügt dem Typen noch zur Laufzeit ein Interface hinzu.
ist das überhaupt richtig?
Mir sieht das Latent-Dings wie etwas aus, was mit Reflection auf Klassenmember zugreift, aber das ist doch kein Interface.

kannst du zur Funktionsweise vom Latent.Cast noch ein paar Worte verlieren?
Ich hab's mir gerade mal genauer angeguckt: Du erstellst einen neuen Typen, der von WrapperBase ableitet, das Ziel-Interface implementiert und das Quelltyp-Exemplar als privates Feld erhält. Dann werden Wrapper-Methoden implementiert, die die entsprechenden Methoden am privaten Feld aufrufen.

Würde es nicht reichen, einen neuen Typ zu erstellen, der vom Quelltyp ableitet und explizit (nur) sagt, dass das Zielinterface implementiert wird (müsste dann zu Fehlern führen, wenn die Methoden nicht da sind). Danach einfach per FormatterServices.GetUninitializedObject ein Exemplar dieses Typs erzeugen und die Felder vom Quellobjekt reinkopieren.
Das würde m.E. dem Cast viel näher kommen. Das bisherige Vorgehen von Latent.Cast ist doch eher ein Convert. Oder?

Beste Grüße,
dN!3L

656 Beiträge seit 2008
vor 14 Jahren

Würde es nicht reichen, einen neuen Typ zu erstellen, der vom Quelltyp ableitet und explizit (nur) sagt, dass das Zielinterface implementiert wird (müsste dann zu Fehlern führen, wenn die Methoden nicht da sind). Danach einfach per FormatterServices.GetUninitializedObject ein Exemplar dieses Typs erzeugen und die Felder vom Quellobjekt reinkopieren.
Das würde m.E. dem Cast viel näher kommen. Das bisherige Vorgehen von Latent.Cast ist doch eher ein Convert. Oder?

...macht aber ein Problem mit sealed Typen, behaupte ich mal ohne es probiert zu haben (anonyme Typen sind nämlich sealed).

Grundsätzlich finde ich die Idee aber interessant, hatte schon mehrmals Fälle wo ein anonymer Typ gleich ausgesehen hat wie ein Interface.

2.891 Beiträge seit 2004
vor 14 Jahren

...macht aber ein Problem mit sealed Typen, behaupte ich mal ohne es probiert zu haben (anonyme Typen sind nämlich sealed).

Oh, stimmt ja. Da sie internal sealed sind, bleibt eigentlich nur dr4g0n76s Vorgehen.

Grundsätzlich finde ich die Idee aber interessant, hatte schon mehrmals Fälle wo ein anonymer Typ gleich ausgesehen hat wie ein Interface.

Ganz interessant finde ich auch das Konzept anonymer Klassen in Java - dort kann (und muss) man von anderen Typen ableiten und auch direkt "ganz anonym" Methoden überschreiben. Beispielsweise:

Zitat von: Java ist auch eine Insel – 6.12 Innere Klassen

  
import java.awt.Point;  
public class InnerToStringPoint  
{  
  public static void main( String[] args )  
  {  
    Point p = new Point( 10, 12 ) {  
      @Override  
      public String toString() {  
        return "(" + x + "," + y + ")";  
      }  
    };  
    System.out.println( p );    // (10,12)  
  }  
}  
  

Manchmal braucht man an einer einzigen Stelle eine Klasse, in der man für einen einzigen Verwendungszweck eine einzige Methode überschreiben muss (z.B. einen SerializationBinder, der einen bestimmten Typ aus dem aktuellen Ausführungskontext zurück gibt). So könnte man (vor allem auch durch Closures) oft viel Quelltext sparen.

Gruß,
dN!3L

0
767 Beiträge seit 2005
vor 14 Jahren

Find ich ein interessantes Projekt, ich versteh das als dynamisch erstellten Adapter (Adapter Pattern). Den könnte man zwar für jede (ausgenommen anonyme) Klasse auch selber schreiben, aber damit gehts schneller.

Interessant wären noch die Performanceunterschiede des Adapters im Vergleich zum Original bzw zu einem "handgeschriebenen" Adapter.

loop:
btst #6,$bfe001
bne.s loop
rts

dr4g0n76 Themenstarter:in
2.921 Beiträge seit 2005
vor 7 Jahren

Als Ergänzung, weil ich gerade wieder über diesen alten Beitrag gestolpert bin.

Falls das jemand noch nicht weiss, was auch noch möglich ist mit Clay (http://clay.codeplex.com/):

Auszug aus

Tales from the Evil Empire - Clay: malleable C# dynamic objects – part 2

There’s one last thing I’d like to show that I found really neat and surprising the first time Louis showed it to me.

Imagine that you have a CLR interface that you need to implement, for example:

public interface IPerson {  
    string FirstName { get; set; }  
    string LastName { get; set; }  
}  
  

but you would like to do it using a Clay object such as one of the persons defined above. Well, you can do this:

IPerson lou = people[0];  
var fullName = lou.FirstName + " " + lou.LastName;  
  

What’s extraordinary here is that lou is a perfectly valid statically typed CLR variable. You’ll get full IntelliSense and compile-time checks on that code. It’s just an object that implements IPerson although we never wrote a concrete type implementing that interface.

What makes the magic possible is that Clay is overriding the cast operator and creating a dynamic proxy for the interface (using Castle) that delegates the members to the Clay object.

So there is an actual CLR type but it’s being code-generated on the fly.

That is what enables you to write the following:

foreach(var person in directory) {  
    Trace.Write(person.FirstName);  
}  

What’s happening here is that the “directory” Clay array is being cast into an IEnumerable, for which all the right methods are implemented by the dynamic Clay array object.

We are going to dig deeper into Clay in future posts as there is so much more to do and hack but that should give a satisfactory introduction of the basic intents for the library. I certainly hope you like it and find some crazy ideas about how to use it.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

T
2.219 Beiträge seit 2008
vor 7 Jahren

@dr4g0n76
Laut der Seite scheint dort seit rund 2011 nichts mehr bei dem Projekt zu laufen.
An sich ist es interessant aber in einem größeren Projekt würde ich dies nicht verwenden wollen.
Ich bin kein Freund davon, wenn der Code generiert wird und ich nicht sehen kann wie die konkreten Typen implementiert sind.
Gerade wenn man auf ein komisches Verhalten trifft oder bestimmte Bedingen bei den Properties implementiert werden müssen, ist solche Code Magie nicht flexibel.

Aber insgesamt ist das Konzept denoch hilfreich um bestimmte Schnittstellen schnell und einfach zu implementieren.
Vorallen bei einfachen Container Klassen, die eben nur Daten ohne Funktionen, implementieren müssen, kann dies Zeit sparen.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.