Kas automatic property on sama mis property?
06.12.2007 | Gunnar
Jõudsin selle huvipakkuva küsimuseni automaatsete omaduste (automatic property) osas tegelikult tükk aega enne tänast. Nimelt jäin sellele mõtlema ühel TechEd'il toimunud ettekandel, millele eelnenud õhtu osutus oodatust hulka meeleolukamaks. See kanne tutvustab tulemusi, mis ilmnesid automaatsete ja tavaliste omaduste võrdlemisel.
Kontrollimise viime läbi järgmise klassi abil, millele lisame hiljem juurde paar huvipakkuvat lisandit.
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
public string Name2 { get; set; }
}
Kompileerime selle klassi nüüd kokku ja vaatame korra, mis tulemust näeme näiteks Reflector'i abil.
{
// Fields
[CompilerGenerated]
private string <name2>k__BackingField;
private string name;
// Methods
public AutoPropTest();
// Properties
public string Name { get; set; }
public string Name2 {
[CompilerGenerated] get;
[CompilerGenerated] set;
}
}
Siit on hästi näha see, et automaatne ja tavaline omadus pole sünonüümid. Automaatsele omadusele vastav atribuut on CompilerGeneratedAttribute. Dokumentatsiooni järgi saab selle lisaatribuudi alusel kontrollida, kas tegemist on koodis deklareeritud atribuudiga või siis kompilaatori poolt loodud atribuudiga.
Samuti on atribuuti tutvustavas ülimalt lakoonilises dokumentatsioonis toodud ära see, et seda lisaatribuuti kasutab näiteks SQL Server selleks, et leida kompilaatori poolt genereeritud staatilisi atribuute. See erinevus on nüüd üks asi.
Teine asi on see, et me ei saa tegeleda automaatsele omadusele vastava atribuudiga otse oma koodis. Põhjus lihtne – kompilaator ei lase läbi muutujaid, mille nimi sisaldab nurksulge. Seda, kuidas võiks ja miks ei saa oma koodis automaatsete omaduste käitumist järgi mängida, tutvustab meile Doron Yaacoby oma blogi kandes C# 3.0 New Feature for the Week #1: Automatic Properties.
Kas kompilaatori poolt genereeritud atribuudile saab ligi?
Järgmiseks proovime siis automaatsele omadusele Name2 vastavat atribuuti oma koodis muuta. Otse me sellele viitada ei saa, sest kompileerimise eel, mil me tegeleme lähtekoodi tasandil, seda atribuuti veel olemas ei ole. Küll aga on see atribuut olemas meil peale kompileerimist. Seega tuleb mängu reflection. Kirjutame klassile juurde Name2Direct omaduse. Kood ise on järgmine.
{
get
{
Type t = this.GetType();
BindingFlags flags = BindingFlags.GetField;
flags = flags | BindingFlags.Instance;
flags = flags | BindingFlags.NonPublic;
return (string)t.InvokeMember("<name2>k__BackingField", flags, null, this, null);
}
set
{
Type t = this.GetType();
BindingFlags flags = BindingFlags.SetField;
flags = flags | BindingFlags.Instance;
flags = flags | BindingFlags.NonPublic;
object[] newValue = new object[] { value };
t.InvokeMember("<name2>k__BackingField", flags, null, this, newValue);
}
}
Järgmiseks proovime, mis juhtub siis, kui püüame väärtustada selle atribuudi otse.
{
static void Main(string[] args)
{
AutoPropTest autoProp = new AutoPropTest();
Console.WriteLine("Väärtus praegu: "+autoProp.Name2);
Console.WriteLine("Omistan JEEE!");
autoProp.Name2Direct = "JEEE!";
Console.WriteLine("Uus väärtus: "+autoProp.Name2);
Console.ReadLine();
}
}
Nagu testimine näitab, siis saame me vajadusel selle atribuudi kätte reflection'i teel. Kus sellist asja võib vaja minna? Näiteks O/R Mapper'id on üks koht, kus teinekord omistatakse väärtusi otse privaatsetele atribuutidele. Kindlasti annab välja mõelda veel igasugu olukordi, kus äkitselt selline võimalus tore oleks, kuid need vahvad fantaasiad jäägu näiteks selle postituse kommentaarideks.
