[C#/Silverlight]Via Reflection een Class extenden

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 10:47
Stel deze class voor:
C#:
1
2
3
4
5
6
7
8
9
10
public class MyCustomClass
{
    public string Name{ get; set; }
    public DateTime MyDate { get; set; }
    public int ItemCol_0 { get; set; }
    public int ItemCol_1 { get; set; }
    //..
    public int ItemCol_20 { get; set; }
    //etc. etc.
}
Deze class dient als DataContainer voor een AgDataGrid.DataSource middels een List<HorizontalDataGridRow>. De ItemCol_0 ... t/m ItemCol_20 worden via Reflection ingelezen waardoor je de HeaderContent van zo'n ItemCol_0 in het Datagrid op iets als "Naam", "Soort" etc. etc. kan zetten.

Het hele ItemCol_0 is leuk totdat er een ItemCol_21 nodig is. Het aanmaken van een compleet nieuw object via de Property- Field- en TypeBuilder lukt wel, alleen deze class is vele malen groter, bevat Custom types, etc. etc. ItemCol_n is altijd van het type Int32.

Ik dacht even snugger te zijn en een extra List<object> MyItems toe te voegen aan de bestaande class en deze via de Property- Field- en TypeBuilder toe te voegen aan deze List<>. Helaas is het AgDataGrid niet in staat om de List<> te gebruiken als een TwoWay bound column.

Kort gezegd: Hoe 'extend/import' ik een bestaande class, via Reflection, met behoud van bestaande properties en voeg ik daar nieuwe dynamische properties aan toe.

Heart..pumps blood.Has nothing to do with emotion! Bored


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
TeeDee schreef op donderdag 19 maart 2009 @ 23:28:
Kort gezegd: Hoe 'extend/import' ik een bestaande class, via Reflection, met behoud van bestaande properties en voeg ik daar nieuwe dynamische properties aan toe.
Ik weet het niet 100% zeker, maar volgens mij kan je via reflection geen classes extenden. Wat volgens mij een optie is ( maar waar ik zelf geen ervaring mee heb ) om je class te inspecteren met reflection en dan D.M.V. IL emitting een sub-class te maken.

Anders zou je eens moeten kijken hoe frameworks als NHibernate Proxy classes aanmaken. Volgens mij gebruiken die daar weer een library voor die jij mischien ook kunt gebruiken.

Overigens is het me niet helemaal duidelijk wat je nu wilt. Je vult een DataGrid d.m.v reflection, maar waarom wil je dan een sub-class maken?

[ Voor 9% gewijzigd door Woy op 20-03-2009 09:01 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 10:47
Woy schreef op vrijdag 20 maart 2009 @ 08:59:
[...]
Overigens is het me niet helemaal duidelijk wat je nu wilt. Je vult een DataGrid d.m.v reflection, maar waarom wil je dan een sub-class maken?
De MyCustomClass uit de TS bevat dus ook de ItemCol_n properties. Daar dit een limitatie is in het aantal items zou ik deze willen verwijderen en alleen de Name / MyDate properties over willen houden en runtime, via reflection de items toevoegen.

Het resultaat wordt dan iets als:
C#:
1
2
3
4
5
6
7
8
9
10
11
public class MyCustomGeneratedClass
{
    public string Name{get;set;}
    public DateTime MyDate{get;set;}

//reflection properties
    public int Plaat {get;set;}
    public int Hoek {get;set;}
    public int Ronding {get;set;}
    public int Meuk {get;set;}
}

Vervolgens gaan we een list maken van het type ObservableCollection<MyCustomGeneratedClass>. Deze Collection knopen we aan de DataSource van het AgDataGrid.

Een Datatable gebruiken hiervoor wordt lastig in Silverlight.

Heart..pumps blood.Has nothing to do with emotion! Bored


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Kan je dan niet gewoon een Dictionary gebruiken en aan de hand daarvan je datagrid opbouwen?

Wat is de meerwaarde van Properties runtime toe voegen?

edit
Ah ik snap het al. Het gaat om het Observable gebeuren. Je datagrid gebuikt de INotifyPropertyChanged methode om zijn update gericht te doen.

Maar wat lukt er niet met de Type/Module Builder class. Want wat ik daar zie, kun je inderdaad gewoon runtime een class maken die extend van je oude class en daar extra properties aan toevoegen.

[ Voor 57% gewijzigd door Woy op 20-03-2009 09:58 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 10:47
Woy schreef op vrijdag 20 maart 2009 @ 09:50:
Maar wat lukt er niet met de Type/Module Builder class. Want wat ik daar zie, kun je inderdaad gewoon runtime een class maken die extend van je oude class en daar extra properties aan toevoegen.
Voor zover ik het voor elkaar gekregen heb is het volledig (incl. bestaande properties) aanmaken van de class in Silverlight. Het extenden/appenden (via welke Reflection methode) krijg ik niet voor elkaar. Geen idee waar ik het moet zoeken.

Heart..pumps blood.Has nothing to do with emotion! Bored


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Je kan toch gewoon extra properties toevoegen met je TypeBuilder
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
foreach (string property in properties)
{
    string fieldName;
    fieldName = Char.ToLower(property[0]) + property.Substring(1);
    
    FieldBuilder fbBackingField = tb.DefineField(
        fieldName,
        typeof (int),
        FieldAttributes.Private);

    PropertyBuilder pbProperty = tb.DefineProperty(
        property,
        PropertyAttributes.HasDefault,
        typeof (int),
        null);

    MethodAttributes getSetAttr = MethodAttributes.Public |
                                  MethodAttributes.SpecialName |
                                  MethodAttributes.HideBySig;

    MethodBuilder mbGetAccessor = tb.DefineMethod(
        "get_" + property,
        getSetAttr,
        typeof (int),
        Type.EmptyTypes);

    ILGenerator getIL = mbGetAccessor.GetILGenerator();
    
    getIL.Emit(OpCodes.Ldarg_0);
    getIL.Emit(OpCodes.Ldfld, fbBackingField);
    getIL.Emit(OpCodes.Ret);

    MethodBuilder mbSetAccessor = tb.DefineMethod(
        "set_" + property,
        getSetAttr,
        null,
        new Type[] {typeof (int)});

    ILGenerator setIL = mbSetAccessor.GetILGenerator();
    
    setIL.Emit(OpCodes.Ldarg_0);
    setIL.Emit(OpCodes.Ldarg_1);
    setIL.Emit(OpCodes.Stfld, fbBackingField);
    setIL.Emit(OpCodes.Ret);

    pbProperty.SetGetMethod(mbGetAccessor);
    pbProperty.SetSetMethod(mbSetAccessor);
}

Je zult nog wel het een en ander toe moeten voegen ( en wat robuster maken ), maar op deze manier kan je gewoon een complete class maken met alles erin wat je wilt.
[disclaimer]
Ik heb de code overigens niet getest, heb een beetje gecopy paste van de msdn ;)
[/disclaimer]

[ Voor 3% gewijzigd door Woy op 20-03-2009 11:13 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 10:47
Met onderstaande method ben ik al een poosje bezig. De return wordt aan een Grid gebonden.
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private List<HorizontalDataGridRow> CreateRunTimeRows()
{
    List<HorizontalDataGridRow> rows = new List<HorizontalDataGridRow>();
    HorizontalDataGridRow row = new HorizontalDataGridRow();
    
    AssemblyName assemblyName = new AssemblyName();
    assemblyName.Name = "AgDataGridAssembly";
    AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(
                        assemblyName, AssemblyBuilderAccess.Run);
    ModuleBuilder module = assemblyBuilder.DefineDynamicModule("AgDataGridtmpModule");
    TypeBuilder typeBuilder = module.DefineType("tmpAgDataGrid", TypeAttributes.Public | TypeAttributes.Class);
            
    /* Voeg nieuwe properties toe aan een bestaande class (HorizontalDataGridRow) */
    /* code ingekort :) */
    foreach (ItemSet item in new ItemSet().Cols())
    {
        /* field- en propertybuilders */
        FieldBuilder field = typeBuilder.DefineField...
        PropertyBuilder property = typeBuilder.DefineProperty..
        / * ilgen zaken */
        property.SetGetMethod(currGetPropMthdBldr);
        property.SetSetMethod(currSetPropMthdBldr);

        //de Columns collectie kan _niet_ aan het Grid gebound worden
        if (row.Columns == null)
        {
            row.Columns = new List<object>();
        }
        row.Columns.Add(property);
    }
    /* 'reeds bestaande properties in HorizontalGridRow */
    row.Direction = "in";
    row.TransactionDate = DateTime.Now;
    rows.Add(row);
    Type generetedType = typeBuilder.CreateType();      
    object generetedObject = Activator.CreateInstance(generetedType);
    PropertyInfo[] properties = generetedType.GetProperties();
    int propertiesCounter = 0;
    foreach (ItemSet item in new ItemSet().Cols())
    {
        string value = "Item" + item.ItemId;
        properties[propertiesCounter].SetValue(generetedObject, 0, null);
        propertiesCounter++;
    }
    return rows;
}

Heart..pumps blood.Has nothing to do with emotion! Bored


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Wat me in je code opvalt is dat je wel een new type maakt, maar de instances die je aan je result toevoegt zijn gewoon van het type HorizontalDataGridRow. Je wilt toch juist instances van je custom type toevoegen?

Wat je volgens mij in pseudocode wilt doen is
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private List<HorizontalDataGridRow> CreateRunTimeRows()
{
    List<HorizontalDataGridRow> rows = new List<HorizontalDataGridRow>();
    Type newType = CreateDynamicType( extraProperties, HorizontalDataGridrow /*Base class*/ );
    foreach( rowToCreate )
    {
        object instance = Activator.CreateInstance( newType );

        foreach( property in extraProperties )
        {
            SetPropertyViaReflection( instance, property );
        }

       HorizontalDataGridrow row = (HorizontalDataGridrow)instance;
       SetNormalProperties( row );
       rows.Add(row);
    }
    return rows;
}

Of ik begrijp nog niet helemaal wat je nu wilt.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 10:47
CreateDynamicType lijkt me de way to go. Daar kon ik niet opkomen. We gaan vanmiddag / vanavond eens stoeien! Dank.
We gaan weer eens verder Googlen :D

[ Voor 16% gewijzigd door TeeDee op 20-03-2009 12:11 ]

Heart..pumps blood.Has nothing to do with emotion! Bored


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
TeeDee schreef op vrijdag 20 maart 2009 @ 12:02:
CreateDynamicType lijkt me de way to go. Daar kon ik niet opkomen. We gaan vanmiddag / vanavond eens stoeien! Dank.
CreateDynamicType is gewoon een methodenaam die ik bedacht heb he :?

In die methode ga je dan aan de slag met je TypeBuilder en dergelijke.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 10:47
Woy schreef op vrijdag 20 maart 2009 @ 12:05:
[...]

CreateDynamicType is gewoon een methodenaam die ik bedacht heb he :?

In die methode ga je dan aan de slag met je TypeBuilder en dergelijke.
Yup, maar kreeg behoorlijk wat results in Google dus leek het me een goede richting.

Heart..pumps blood.Has nothing to do with emotion! Bored


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
TeeDee schreef op vrijdag 20 maart 2009 @ 12:14:
[...]

Yup, maar kreeg behoorlijk wat results in Google dus leek het me een goede richting.
Ik had het idee dat je al redelijk op weg was met zelf een dynamic type maken. Dat er in google veel hits komen is omdat de methode naam gewoon goed beschrijft wat je moet doen ;)

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 10:47
Hmm, het e.e.a. vergt nog even wat werk, want de aangepaste type HorizontalDataGridRow is vervolgens niet meer te binden. Waarschijnlijk gaat dit wel in het default Datagrid (meer info hier te vinden).

Heart..pumps blood.Has nothing to do with emotion! Bored


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
TeeDee schreef op vrijdag 20 maart 2009 @ 15:05:
Hmm, het e.e.a. vergt nog even wat werk, want de aangepaste type HorizontalDataGridRow is vervolgens niet meer te binden.
Hoezo is deze niet meer te binden? Ook al heb je het type run-time gemaakt, verder is het geen "speciaal" object ofzo.
Waarschijnlijk gaat dit wel in het default Datagrid (meer info hier te vinden).
De link die je aanhaalt gaat over het binden van een Dictionary, dat is een van de oplossingen die ik eerst ook aandroeg
Woy schreef op vrijdag 20 maart 2009 @ 09:50:
Kan je dan niet gewoon een Dictionary gebruiken en aan de hand daarvan je datagrid opbouwen?
Maar dat zal je datagrid dus wel moeten ondersteunen. Ik weet niet precies hoe de control die jij gebruikt werkt, Mischien kan je daar ook wel iets met Binding's doen.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 10:47
Woy schreef op vrijdag 20 maart 2009 @ 15:26:
[...]

Hoezo is deze niet meer te binden? Ook al heb je het type run-time gemaakt, verder is het geen "speciaal" object ofzo.
Het AgDataGrid is op dit gebied nog redelijk gehandicapt.
De link die je aanhaalt gaat over het binden van een Dictionary, dat is een van de oplossingen die ik eerst ook aandroeg
[...]
Door het gebruik van deze class is het hele TwoWay binding (dus de observablecollection) weg op 1 of andere manier.
Maar dat zal je datagrid dus wel moeten ondersteunen. Ik weet niet precies hoe de control die jij gebruikt werkt, Mischien kan je daar ook wel iets met Binding's doen.
De binding geschiedt door de "FieldName" property te setten en magisch wordt het e.e.a. gedaan.

Ik kan wel prima (volgens mijn Immediate Window) netjes properties toevoegen, alleen deze zijn niet bound. Het kan (en dat acht ik mogelijker) dat er nog een fout in de Property generatie zit (de Debugger van VS kan er ook geen kaas van maken) dus daar ga ik nog eens rustig voor zitten en vanaf de grond af opnieuw beginnen.

Heart..pumps blood.Has nothing to do with emotion! Bored


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
TeeDee schreef op vrijdag 20 maart 2009 @ 16:04:
[...]
Het AgDataGrid is op dit gebied nog redelijk gehandicapt.
Maar wat is er dan gehandicapt aan? Ik zie nog geen reden waarom een van te voren gecompileerde class zich anders zou moeten gedragen dan een runtime-gegeneerde class.
Ik kan wel prima (volgens mijn Immediate Window) netjes properties toevoegen, alleen deze zijn niet bound. Het kan (en dat acht ik mogelijker) dat er nog een fout in de Property generatie zit (de Debugger van VS kan er ook geen kaas van maken) dus daar ga ik nog eens rustig voor zitten en vanaf de grond af opnieuw beginnen.
Schrijf je dynamische assembly dan eens gewoon weg, en probeer hem met reflector te openen. Als reflector het ook niet snapt heb je waarschijnlijk iets verkeerd gedaan.

Als je niet precies weet welke IL je moet emitten dan kan je eerst een keer handmatig een class maken en deze compilen, door middel van Reflector of ILDasm kan je dan kijken welke IL dat oplevert en die kan je dan kopieren.

Ik heb het overigens even geprobeerd, en als ik dynamisch een type maak dan kan ik ook in de debugger gewoon al zijn properties opvragen en zetten. Ook via reflection kan ik dat gewoon doen. Het gedraagd zich dus gewoon als normaal object, behalve dat je compile time nog geen definitie hebt.

[ Voor 22% gewijzigd door Woy op 20-03-2009 16:14 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Ik durf niet te zeggen of je nu ook echt dynamisch een type aan moet maken, of dat er betere oplossingen zijn, maar:

Als je dynamisch code wil genereren, dan zijn er twee manieren: via Reflection.Emit (de "TypeBuilder"-manier) of via CodeDom (een vergelijking staat hier).

Ik heb persoonlijk geen ervaring met Reflection.Emit, maar wel (goede) ervaringen met CodeDom. Het idee is dat je met types uit de System.CodeDom namespace een DOM van je code opbouwt, en deze vervolgens door een codeprovider ofwel wegschrijft als C# source, of on-the-fly laat compileren tot een assembly die je vervolgens kunt gebruiken - zie het voorbeeld hier.

En mocht je het quick 'n dirty willen doen, de CodeSnippetCompileUnit, CodeSnippetTypeMember, CodeSnippetStatement en CodeSnippetExpression types staan je toe een codefragment als platte tekst aan te leveren ipv alles op te bouwen via een DOM. Dit gaat een stuk sneller, maar hiermee verlies je wel de mogelijkheid om je code als VB.NET source weg te schrijven.
Pagina: 1