[C#] de nieuwe features in versie 3.0

Pagina: 1
Acties:

  • beany
  • Registratie: Juni 2001
  • Laatst online: 19:51

beany

Meeheheheheh

Topicstarter
Terwijl het .NET 2.0 framework officieel nog in het beta stadium is(maar heel binnenkort gereleased wordt) is Microsoft al weer een tijdje bezig met de volgende ronde voor te bereiden. C# 3.0, rijk aan nieuwe features die het ons als programmeurs weer een stuk makkelijk zal gaan maken. Tenminste, is dat echt zo?

1 van de dingen die me opviel in de 3.0 specificaties is implicitly typed local variables. Dit houd in dat een gedefineerde variable pas bij het compilen zijn type toegekend zal krijgen. In C# werkt het zo:

code:
1
2
3
var i = 5;
var s = "Hello";
var d = 1.0;


De compiler zal het rechtzetten, en tijdens het compileren van de variabel i een int maken. Het is dus niet een var zoals we gewend zijn in javascript!

Naar mijn idee zal deze feature van C# 3.0 alleen maar programmeurs verleiden tot onoverzichtelijke code. Ik snap eigenlijk ook niet goed waarom microsoft dit in C# 3.0 gaat stoppen? Ik krijg weer VB6 nachtmerries(het type variant) :'(

Zijn er ook nog positieve zaken aan implicitly typed local variables? Zitten we hier echt op te wachten?

Voor andere nieuwe zaken in C# 3.0, kijk even bij microsoft of voor een wat heldere uitleg kan je bij Ted Neward op zijn blog kijken

Dagelijkse stats bronnen: https://x.com/GeneralStaffUA en https://www.facebook.com/GeneralStaff.ua


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 22:29

curry684

left part of the evil twins

Anonymous types
Combining the above two features brings us to an interesting conclusion: if we are teaching the compiler to infer static type information and provide some basic defaults for types, then we can actually expect some fairly interesting intuition on the part of the compiler now--in particular, the compiler is now smart enough to be able to infer an entire type during compilation. Thanks to the object-initializer syntax (to provide the necessary constructor capabilities) and the implicitly-typed local variable syntax (to be able to avoid having to name the type), we can write the following and expect a statically-typed class out of it:

C#:
1
2
var x = new { UpperLeft = new Point { X = 0, Y = 0 },
              LowerRight = new Point { X = 5, Y = 5 } };


Again, thanks to the initalizer syntax, the compiler now has enough information to be able to auto-generate the following:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
class __This_Name_Really_Doesnt_Matter
{
  private Point _Field1;
  private Point _Field2;

  public Point UpperLeft { get { return _Field1; } set { _Field1 = value; } }
  public Point LowerRight { get { return _Field2; } set { _Field2 = value; } }

  public override bool Equals(bool rhs) { ... }
  public override string ToString() { ... }
  public override int HashCode() { ... }
}


which, if you think about it, is pretty cool. Project DLinq, the relational access project Microsoft introduced at PDC, will use this to address the partial query problem that plagues automated object-relational mapping layers, as now we can introduce new types into the system (as return types from an ad-hoc query) in just a line or two of code, rather than the twenty or so that would otherwise be required.
Daar heb je je extreem coole effect van implicitly typed local variables :)

[ Voor 4% gewijzigd door curry684 op 21-10-2005 10:27 ]

Professionele website nodig?


  • beany
  • Registratie: Juni 2001
  • Laatst online: 19:51

beany

Meeheheheheh

Topicstarter
Dat is inderdaad erg cool. Maar toch ben ik bang dat het programmeurs zal uitnodigen om niet de moeite te nemen om hun variabelen correct te defineren... Ofterwel, de var niet correct gebruiken.

Dagelijkse stats bronnen: https://x.com/GeneralStaffUA en https://www.facebook.com/GeneralStaff.ua


  • 6K
  • Registratie: September 2002
  • Laatst online: 19-01-2025

6K

is ook zo...

OMG... dit is wel het laatste wat ik zou willen >.<

ik bedoel, ik zie al weer veel programmeurs standaard die var gaan gebruiken om me heen.

[ Voor 49% gewijzigd door 6K op 21-10-2005 10:37 ]

٩(͡๏̯͡๏)۶ ٩(●̮̮̃•̃)۶


Verwijderd

Deze nieuwe variabele is nodig icm met linq dacht ik (zoek het even op)

Ben ook bang dat var de nieuwe meest gebruikte variabele gaat worden. Maar wat hier staat is toch heel vernieuwend en opent heel veel mogelijkheden.

Dan Fernandez's blog
http://blogs.msdn.com/danielfe/archive/category/10786.aspx

lees vooral deze post:
http://blogs.msdn.com/dan...ve/2005/09/22/472884.aspx

[ Voor 70% gewijzigd door Verwijderd op 21-10-2005 11:07 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 23:03
6K schreef op vrijdag 21 oktober 2005 @ 10:36:
OMG... dit is wel het laatste wat ik zou willen >.<

ik bedoel, ik zie al weer veel programmeurs standaard die var gaan gebruiken om me heen.
Ik denk dat dit nogal zal meevallen, aangezien je de variable moet initializeren bij de declaratie, en je ze niet op null mag initializeren.
Eens ze geinitialiseerd is, kan ze afaik ook enkel types bevatten van dat type waarop je ze ge-init hebt.

Ik snap wel het nut (nog) niet van die extension methods. :?

[ Voor 7% gewijzigd door whoami op 21-10-2005 11:10 ]

https://fgheysels.github.io/


  • Exirion
  • Registratie: Februari 2000
  • Laatst online: 22:51

Exirion

Gadgetfetisjist

Like... back to the BASICs... Letterlijk :{

"Logica brengt je van A naar B, verbeelding brengt je overal." - Albert Einstein


  • Eelke Spaak
  • Registratie: Juni 2001
  • Laatst online: 25-04 12:33

Eelke Spaak

- Vlad -

whoami schreef op vrijdag 21 oktober 2005 @ 11:10:
[...]
Ik snap wel het nut (nog) niet van die extension methods. :?
Ik ben weliswaar geen C# programmeur, maar naar aanleiding van dit topic ben ik even wat gaan bijlezen over nieuwe ontwikkelingen op dit vlak. Die Extension Methods lijken me een (imho onwenselijke) vorm van syntactic sugar voor methode-aanroepen, maar een gerelateerde nieuwe introductie in de taal is wel zeer interessant; deze vormde misschien ook de aanleiding tot het introduceren van Extension Methods.

Wat me opviel toen ik zo wat aan het browsen was, was dat C# sinds 3.0 closures ondersteunt. Dit is wat mij betreft redelijk revolutionair. Ik ben aardig bekend met closures door talen als Scheme, die gebaseerd zijn op de lambda calculus. Heel kort door de bocht gezegd kan je methoden (oftewel lambda-expressies) als argumenten meegeven aan andere methoden, en die methoden ook weer methoden laten opleveren.

Ik weet niet wanneer C# 3.0 in productie gaat, maar dit kan voor de serieuze (waarschijnlijk academische) programmeur een belangrijke reden zijn om C# te verkiezen boven Java.

TheStreme - Share anything with anyone


  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

whoami schreef op vrijdag 21 oktober 2005 @ 11:10:
[...]

Ik snap wel het nut (nog) niet van die extension methods. :?
Misschien voor klasses waar je zelf niet aan kan/mag sleutelen en waarop je toch vaak dezelfde methode uit wilt voeren?

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • beany
  • Registratie: Juni 2001
  • Laatst online: 19:51

beany

Meeheheheheh

Topicstarter
Eelke Spaak schreef op vrijdag 21 oktober 2005 @ 11:43:
[...]
Wat me opviel toen ik zo wat aan het browsen was, was dat C# sinds 3.0 closures ondersteunt. Dit is wat mij betreft redelijk revolutionair. Ik ben aardig bekend met closures door talen als Scheme, die gebaseerd zijn op de lambda calculus. Heel kort door de bocht gezegd kan je methoden (oftewel lambda-expressies) als argumenten meegeven aan andere methoden, en die methoden ook weer methoden laten opleveren.
Zou je dit wat meer in simpelere taal kunnen uitleggen? Wat kan je hiermee? Wanneer gebruik je dit?

Dagelijkse stats bronnen: https://x.com/GeneralStaffUA en https://www.facebook.com/GeneralStaff.ua


  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 20-10-2025

D4Skunk

Kind of Blue

De hoofdreden van var : het vermijden van casts :
C#:
1
2
3
4
// oude code zou zijn : 
List<ComboBox> a=new List<ComboBox>();
//nieuwe code is :
var b=new List<ComboBox>();


Extension methods vind ik net zeer goed, indien je ze verstandig gebruikt.
Persoonlijk zie ik vooral mogelijkheden naar het implementeren van DSL's etc. Merk op dat tot nu toe bijna alle wijzigingen voor c# 3.0 gewoon 'syntactic sugar' zijn.

Veronderstel dat je er voor zorgt dat al je businessobjecten IErrorProvider implementeren, en maak dan de volgende code :
C#:
1
2
3
4
5
6
7
public static IErrorProvider<T> ShowError<T>(this IErrorProvider<T> source) {
  // zet error in errorlist
}

public static IErrorProvider<T> TranslateError<T>(this IErrorProvider<T> source) {
  // vertaal error msg
}


Dan kan je daarna de volgende code aanroepen :
C#:
1
2
3
4
5
6
7
8
  var k = Dm.LoadKlant("D4skunk");
  var b = Dm.LoadBedrijf("Tweakers bv");
  // doe hier vanalles
  k.ControleerGegevens();
  // oude code zou zijn : 
  ShowError(TranslateError(k));
  //nieuwe code is :
  k.TranslateError().ShowError();


Veronderstel dat nu de functie TranslateError en ShowError beiden nog een parameter hebben :
C#:
1
2
3
4
5
6
7
public static IErrorProvider<T> ShowError<T>(this IErrorProvider<T> source,UiProvider ui) {
  // zet error in errorlist
}

public static IErrorProvider<T> TranslateError<T>(this IErrorProvider<T> source,LangEnum lang) {
  // vertaal error msg
}


Dan wordt het verschil al iets duidelijker :
C#:
1
2
3
4
  // oude code zou zijn : 
  ShowError(TranslateError(k,LangEnum.NL),uiListbox);
  //nieuwe code is :
  k.TranslateError(LangEnum.nl).ShowError(uiListBox);
beany schreef op vrijdag 21 oktober 2005 @ 11:52:
[...]

Closures...
Zou je dit wat meer in simpelere taal kunnen uitleggen? Wat kan je hiermee? Wanneer gebruik je dit?
Closures waren al een hele tijd aanwezig in c# 2.0, in versie 3 worden ze enkel wat eenvoudiger te beschrijven, dmv lambda expressions. Een closure is identiek aan een anonymous function pointer, maw een delegate naar een anoniem codeblok.
In versie 1 kon je geen closures definieren, omdat je geen access had naar de variabelen uit de lokale scope.
In versie 2 kon je dat wel, maar het is nog wat omslachtig.
Versie 3 gaat nu echt de goede weg op.

Wat me trouwens opvalt is dat we steeds meer en meer naar de ruby-syntax evolueren.

[ Voor 49% gewijzigd door D4Skunk op 21-10-2005 12:26 ]


  • Eelke Spaak
  • Registratie: Juni 2001
  • Laatst online: 25-04 12:33

Eelke Spaak

- Vlad -

beany schreef op vrijdag 21 oktober 2005 @ 11:52:
[...]


Zou je dit wat meer in simpelere taal kunnen uitleggen? Wat kan je hiermee? Wanneer gebruik je dit?
Ik weet niet precies hoe C# het syntactisch gaat implementeren, maar in Scheme bijvoorbeeld, kan je schrijven (let op: het is prefix-notatie, dus de operator komt vóór de operanden):
Scheme:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
; eerst een algemeen voorbeeld:

((lambda (x) (+ x 1)) 2) ; dit levert 3 op

; om dit te begrijpen, even kort de scheme syntax:
; (functie argumenten)
; dus haakje openen geeft aan dat er een functie komt, daarna een spatie, daarna
; de argumenten van de functie, daarna een haakje sluiten.
; Een functie kan je maken met een lambda-expressie, die ziet er zo uit:
; (lambda (x) (inhoud van de functie))
; en dat is de volledige scheme-syntax :)

; Mijn voorbeeld hierboven valt dus te analyseren als volgt:

((lambda (x) (+ x 1)) 2)
 ^^^^^^^^^^^^^^^^^^^^ ^
   functie            argument

Het volledige concept is misschien wat te ingewikkeld om het hier uit te leggen, maar wat je ermee kan is vrij veel: je kan bijvoorbeeld alle natuurlijke getallen en boolean logica uitdrukken met alleen maar lambda expressies ;) . In de meer directe praktijk stelt het je in staat om, zoals reeds genoemd, methoden mee te geven als argumenten aan andere methoden. Zie dit blogstukje van Martin Fowler voor de praktische voordelen van closures.

TheStreme - Share anything with anyone


  • ThunderNet
  • Registratie: Juni 2004
  • Laatst online: 16:59

ThunderNet

Flits!

Dus als ik het goed begrijp
heb je bijvoorbeeld een functie met 2 parameters

functie (int x, int y);

en doe je dit: functie(anderefunctie(), y);
:?
is dat niet altijd al mogelijk? of zit ik toch fout?

Heb je liever vooraf, of achteraf, dat ik zeg dat ik geen flauw idee heb wat ik doe?


  • TeeDee
  • Registratie: Februari 2001
  • Laatst online: 20:01

TeeDee

CQB 241

ThunderNet schreef op vrijdag 21 oktober 2005 @ 12:10:
Dus als ik het goed begrijp
heb je bijvoorbeeld een functie met 2 parameters

functie (int x, int y);

en doe je dit: functie(anderefunctie(), y);
:?
is dat niet altijd al mogelijk? of zit ik toch fout?
Inderdaad. Zo lees ik het ook.

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


  • beany
  • Registratie: Juni 2001
  • Laatst online: 19:51

beany

Meeheheheheh

Topicstarter
Hmmm, heeft dit ook te maken met anonieme funties?

code:
1
button1.Click += delegate { MessageBox.Show( "Click!") };


Want dat kan namelijk in C# 2.0... zouden ze dat dan in 3.0 hebben uitgebreid?

Dagelijkse stats bronnen: https://x.com/GeneralStaffUA en https://www.facebook.com/GeneralStaff.ua


  • Eelke Spaak
  • Registratie: Juni 2001
  • Laatst online: 25-04 12:33

Eelke Spaak

- Vlad -

ThunderNet schreef op vrijdag 21 oktober 2005 @ 12:10:
Dus als ik het goed begrijp
heb je bijvoorbeeld een functie met 2 parameters

functie (int x, int y);

en doe je dit: functie(anderefunctie(), y);
:?
is dat niet altijd al mogelijk? of zit ik toch fout?
Nee, want anderefunctie() is een functie-aanroep (getuige de haakjes), in plaats van een referentie naar een functie. Wat je kan schrijven is (pseudo-code):
code:
1
2
3
4
5
6
7
8
9
10
functie maakClassificeerder(criterium, waarde)
{
    return new functie(x)
        {
            return (x criterium waarde);
        };
}

functie kleinerDan50 = maakClassificeerder(<, 50);
kleinerDan50(30);  // levert true


Hierbij wordt er een functie (kleiner-dan, <) meegegeven aan de functie maakClassificeerder, die op zijn beurt weer een functie oplevert, die je kan toepassen op getallen.

[ Voor 11% gewijzigd door Eelke Spaak op 21-10-2005 12:21 ]

TheStreme - Share anything with anyone


  • ThunderNet
  • Registratie: Juni 2004
  • Laatst online: 16:59

ThunderNet

Flits!

oh, dus je geeft een functie terug..

dus zelfde principe als in C een pointer naar een functie retourneren? :)

Heb je liever vooraf, of achteraf, dat ik zeg dat ik geen flauw idee heb wat ik doe?


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 22:29

curry684

left part of the evil twins

Voor zover mij bekend is een closure in principe een delegate zonder de delegate-wrapper. Vergelijk:
C#:
1
myClass.SomeFunc(new MyCustomDelegate(MyClassMethod), y);

met:
C#:
1
myClass.SomeFunc(MyClassMethod, y);

De 2e notatie is veel helderder, en werd door Borland C++ Builder en Delphi al gebruikt om events te gebruiken ipv delegates. Ze heetten daar ook closures ;)

In het geval van events kan ik delegates zeer goed verdedigen, met name wegens het multicast aspect. Maar voor bijvoorbeeld een thread method vind ik de huidige syntax met new ThreadStart blahblah gewoon ronduit lelijk, en dat wordt met closures een stuk netter.

In C++ termen gesproken is een closure een function pointer en een instance pointer in 1, waarmee je dus niet alleen static of global methods kunt opslaan of doorgeven maar ook class methods uit 1 specifieke instance.

Professionele website nodig?


  • ThunderNet
  • Registratie: Juni 2004
  • Laatst online: 16:59

ThunderNet

Flits!

Ok, nu begint het me beetje duidelijk te worden :)

Heb je liever vooraf, of achteraf, dat ik zeg dat ik geen flauw idee heb wat ik doe?


  • D4Skunk
  • Registratie: Juni 2003
  • Laatst online: 20-10-2025

D4Skunk

Kind of Blue

Voor wat closures betreft :

uit Martin fowlers bliki
So the first crucial point about closures is that they are a block of code plus the bindings to the environment they came from. This is the formal thing that sets closures apart from function pointers and similar techniques.(Java's anonymous inner classes can access locals - but only if they are final.)

The second difference is less of a defined formal difference, but is just as important, if not more so in practice. Languages that support closures allow you to define them with very little syntax. While this might not seem an important point, I believe it's crucial - it's the key to make it natural to use them frequently. Look at Lisp, Smalltalk, or Ruby code and you'll see closures all over the place - much more frequently used than the similar structures in other languages. The ability to bind to local variables is part of that, but I think the biggest reason is that the notation to use them is simple and clear.
@curry : Het loopt net veeeel verder dan de klassieke anonymous delegate, anders zou er niet zoveel ophef over gemaakt worden; je kan nl in je block verwijzen naar lokale vars, daar zit dus het verschil

[ Voor 13% gewijzigd door D4Skunk op 21-10-2005 12:41 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17:52

.oisyn

Moderator Devschuur®

Demotivational Speaker

beany schreef op vrijdag 21 oktober 2005 @ 10:13:
1 van de dingen die me opviel in de 3.0 specificaties is implicitly typed local variables.
Nice, ik las er in de C++ todo list ook wat over, en vooral in C++ is het handig dat het komt. Maar goed, C++ updates duren zo fucking lang en dan moet je ook nog eens wachten op de compiler vendors :/

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • beany
  • Registratie: Juni 2001
  • Laatst online: 19:51

beany

Meeheheheheh

Topicstarter
even aan het pielen geweest(vs2005 beta 2) en dit is wel erg gaaf hoor!!

Een heel simpel voorbeeldje, je kan zelf wel bedenken dat dit in complexere gevallen erg nuttig is!

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
    public delegate string StringBewerker( string s );

    class Program
    {
        static void Main( string[] args )
        {

            StringBewerker upper = delegate( string s )
            {
                return s.ToUpper();
            };

            StringBewerker lower = delegate( string s )
            {
                return s.ToLower();
            };

            StringBewerker upperorlower;

            if ( SomeBoolIndicatingWeShouldDoUpperCaseTransforming )
                upperorlower = upper;
            else
                upperorlower = lower;

            //vanaf hier hebben we dus bepaald welke bewerking we willen hebben, 
            //en kunnen we dat toepassen zonder if's of switches.
            Console.WriteLine( upperorlower( "BoooHoooooo" ) );
        }
    }


Dit kan lange en complexe switch/if constructies een stuk verminderen! Afhankelijk van variabelen eenvoudig bepalen welke bewerkingen er uitgevoerd moeten worden. Dit is gewoon erg gaaf... Blij dat dit in C# 2.0 zit!!

[ Voor 3% gewijzigd door beany op 21-10-2005 14:03 ]

Dagelijkse stats bronnen: https://x.com/GeneralStaffUA en https://www.facebook.com/GeneralStaff.ua


Verwijderd

.oisyn schreef op vrijdag 21 oktober 2005 @ 12:43:
[...]

Nice, ik las er in de C++ todo list ook wat over, en vooral in C++ is het handig dat het komt. Maar goed, C++ updates duren zo fucking lang en dan moet je ook nog eens wachten op de compiler vendors :/
Toen ik dit topic las dacht ik er ook net aan. In de CUJ van een paar maanden terug stond er een artikel over. Wel jammer dat het C++ kamp altijd zo veel tijd nodig heeft. Het kan nog echt -jaren- duren voordat de nieuwe standaard er daadwerkelijk is. 0x zal wellicht 1x gaan worden.

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:03
beany schreef op vrijdag 21 oktober 2005 @ 14:03:
even aan het pielen geweest(vs2005 beta 2) en dit is wel erg gaaf hoor!!

Een heel simpel voorbeeldje, je kan zelf wel bedenken dat dit in complexere gevallen erg nuttig is!

Dit kan lange en complexe switch/if constructies een stuk verminderen! Afhankelijk van variabelen eenvoudig bepalen welke bewerkingen er uitgevoerd moeten worden. Dit is gewoon erg gaaf... Blij dat dit in C# 2.0 zit!!
Tja, da's een beetje vergelijkbaar met een strategy pattern.

https://fgheysels.github.io/


  • EfBe
  • Registratie: Januari 2000
  • Niet online
beany schreef op vrijdag 21 oktober 2005 @ 14:03:
even aan het pielen geweest(vs2005 beta 2) en dit is wel erg gaaf hoor!!

Een heel simpel voorbeeldje, je kan zelf wel bedenken dat dit in complexere gevallen erg nuttig is!

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
    public delegate string StringBewerker( string s );

    class Program
    {
        static void Main( string[] args )
        {

            StringBewerker upper = delegate( string s )
            {
                return s.ToUpper();
            };

            StringBewerker lower = delegate( string s )
            {
                return s.ToLower();
            };

            StringBewerker upperorlower;

            if ( SomeBoolIndicatingWeShouldDoUpperCaseTransforming )
                upperorlower = upper;
            else
                upperorlower = lower;

            //vanaf hier hebben we dus bepaald welke bewerking we willen hebben, 
            //en kunnen we dat toepassen zonder if's of switches.
            Console.WriteLine( upperorlower( "BoooHoooooo" ) );
        }
    }


Dit kan lange en complexe switch/if constructies een stuk verminderen! Afhankelijk van variabelen eenvoudig bepalen welke bewerkingen er uitgevoerd moeten worden. Dit is gewoon erg gaaf... Blij dat dit in C# 2.0 zit!!
Nou, delegates zijn niet echt rap vergeleken bij normale code. Dus vermijdt indien mogelijk. (Invoke..)

dat var gebeuren... dat is nodig om on the fly nieuwe types te definieren als results van filters op IEnumerable<T> en onderliggende data. Objects selecteren is helder, maar 2 fields uit de Customer class selecteren voor 100 customers, levert een lijst op van 2 fields en 100 rows. Die typed opslaan lukt niet zonder een nieuw type.

Dat doet var. De naam wijzigt wellicht nog.

Over de functional programming spullen die ze erin stoppen... ik weet het niet. Ik ben wel blij met de mogelijkheid om indirect methods te definieren op types, zodat een standaard O/R mapper query language eindelijk mogelijk is, maar ik snap niet echt de drang naar 'ruby' like zaken in C#, als je ruby of weet ik wat wilt gebruiken, gebruik dan dat en niet C#. IMHO kan die tijd beter worden besteed in het oplossen van mixin problemen voor MI zodat dat eindelijk kan worden ingebouwd, pre/post conditions e.d.

[ Voor 25% gewijzigd door EfBe op 21-10-2005 17:05 ]

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com

Pagina: 1