Információ a hívó kódról C# 5-ben

A bejegyzés címe akár az is lehetett volna, hogy “a másik C# 5.0-újdonság”. Az alábbiakban tehát azt mutatom be, hogyan készíthetünk olyan metódusokat C#-ban, melyek információval rendelkeznek az őket meghívó kódról.

Előfordul bizonyos helyzetekben, hogy jól jönne, ha a komponenseink egy-egy metódusát meghívó kód automatikusan információkat adna át saját magáról a meghívott metódusnak. A leghétköznapibb példa talán az INotifyPropertyChanged interfész megvalósítása, amikoris a tulajdonságok set accessorjeiből meghívunk egy metódust, melynek – általában – stringként átadjuk a tulajdonság nevét.

private int count;
public int Count
{
    get { return count; }
    set
    {
        if (count != value)
        {
            count = value;
            RaisePropertyChanged("Count");
        }
    }
}

Mennyivel egyszerűbb lenne ez, ha csak meg kellene hívni a metódust, és az rögtön tudná, hogy ki hívta meg…

Lehet persze más példákat is hozni, például naplózást, nyomkövetést, ahol ennél több információra is szükségünk lehet.

A C# 5.0 egyik újdonsága, hogy bizonyos fokig támogatja ezeknek az adatátadásoknak az automatizálását, egyszerűsítve ezzel a kódunkat. Ez az új fordító-képesség annyira egyszerű, hogy rögtön vágjunk is a közepébe!

using System;
using System.Runtime.CompilerServices;

namespace CS5CallerInfoSample
{
    public class CallerInfo
    {
        public static void GetCallerInfo(
            [CallerFilePath]string filePath = "",
            [CallerLineNumber]int line = 0,
            [CallerMemberName]string member = "")
        {
            Console.WriteLine("Path:\t{0}\nLine:\t{1}\nMember:\t{2}",
                filePath, line, member);
        }
    }
}

A fentebb látható metódus három opcionális paraméterrel rendelkezik. Eddig semmi új, az opcionális paraméterek a C# 4.0-ban érkeztek. Viszont ezeket a paramétereket megjelöltük egy-egy attribútummal. Mi történik, ha meghívják ezt a metódust anélkül, hogy a hívásnál értéket adnának a paramétereknek? Lássuk!

using System;

namespace CS5CallerInfoSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("MyProperty: " + MyProperty);
            Console.Read();
        }

        public static int MyProperty
        {
            get
            {
                CallerInfo.GetCallerInfo();
                return 12;
            }
        }
    }
}

Az előbbi programot futtatva valami hasonló kimenetet kapunk (a fájl elérési útja mindenkinél más lesz):

Path: c:\Users\David\Documents\Visual Studio 2012\Projects
\CS5CallerInfoSample\CS5CallerInfoSample\Program.cs
Line: 18
Member: MyProperty
MyProperty: 12

Nem adtunk értéket a paramétereknek, tehát üres stringeknek és egy 0-nak kellene megjelennie, de mégsem. Varázslat? Igen, de ezt a D&D-n kívül “compiler feature”-nek nevezik. 🙂 (És vannak ennél jóval nagyobbak is, lásd az előző cikket.)

Mellesleg azt is észrevehettük, hogy van annyira “okos” a fordító, hogy pl. egy tulajdonság accessorjében nem a tényleges hívót írja be (ez itt a get_MyProperty metódus lett volna), hanem magát a tulajdonságot.

Ha megnyitjuk a MyPropertyhez tartozó get metódus tartalmát IL-ben, ezt láthatjuk (release fordítás, egy picit áttördelve, hogy kiférjenek a hosszú sorok):

.method public hidebysig specialname static
        int32  get_MyProperty() cil managed
{
  // Code size       20 (0x14)
  .maxstack  8
  IL_0000:  ldstr      "c:\\Users\\David\\Documents\\Visual Studio 2012\\"
  + "Projects\\CS5CallerInfoSample\\CS5CallerInfoSample\\Program.cs"
  IL_0005:  ldc.i4.s   18
  IL_0007:  ldstr      "MyProperty"
  IL_000c:  call       void CS5CallerInfoSample.CallerInfo::GetCallerInfo(
                                                                   string,
                                                                   int32,
                                                                   string)
  IL_0011:  ldc.i4.s   12
  IL_0013:  ret
} // end of method Program::get_MyProperty

Ahogy látható, annyi történik, hogy a fordító egyszerűen betölti a metódushívás előtt a három szükséges információt a stackre. Nem futásidőben történik tehát az adatok kiszámolása, hiszen az teljesen felesleges lenne, ráadásul lassabb is, plusz a hívás sorának a száma és a kódot tartalmazó fájl elérési útja csak C#-ban értelmezhető, a fordítás után, vagy egy másik gépen már nem sokat jelentene ez a két adat.

Ennyi. 🙂

Igazából úgy lenne szép ez a dolog, ha készíthetnénk saját attribútumokat, melyekkel hasonló (vagy akár teljesen más) információkat is beleinjektálhatnánk egy-egy metódushívásba… de ez még a jövő zenéje. Szerencsére úgy tűnik, hogy a nem túl távoli jövőé… 😉

…éééés most vissza kódolni.

Advertisements

~ Szerző: Fülöp Dávid - 2012. október 17..

2 hozzászólás to “Információ a hívó kódról C# 5-ben”

  1. “Igazából úgy lenne szép ez a dolog, ha készíthetnénk saját attribútumokat, melyekkel hasonló (vagy akár teljesen más) információkat is beleinjektálhatnánk egy-egy metódushívásba… de ez még a jövő zenéje. Szerencsére úgy tűnik, hogy a nem túl távoli jövőé… ”

    http://www.sharpcrafters.com/

  2. A PostSharp szívemcsücske, de sajnos “csak” egy 3rd party tool, ami IL rewritinggal operál.
    Amire utalni próbáltam az utolsó mondattal, hogy nem sokára remélhetőleg egy mindenféle dísztől mentes VS-ben is megtehetjük ugyanezt, mégpedig úgy, hogy beleszólhatunk a fordítás folyamatába, és ha pl. egy metóduson rajta van egy attribútum, teleszórhatjuk a metódust infrastrukturális kóddal, még az előtt, hogy lefordulna. Szóval a Roslynra próbáltam utalni. 🙂

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s

 
%d blogger ezt kedveli: