2013-01-21 26 views
7

C# 4.0 specyfikacje przeczytać:typu Runtime vs kompilacji metody typ inwokacją

Gdy wirtualna metoda jest wywoływana, rodzaj runtime przykład dla którym wywołanie odbywa określa rzeczywistą realizację metoda przywoływać. W przypadku wywoływania metody nie-wirtualnej czynnikiem decydującym jest typ kompilacji instancji typu .

Początkowo myślałem, że to ma coś wspólnego z inicjalizacją. Na przykład, biorąc pod uwagę dwa pliki uruchamiania:

BaseClass bcDerived = new Derived(); vs BaseClass bcBase = new BaseClass();

i przeciążenie w klasie pomocnika:

public virtual void Method(Derived d) 
{ 
    Console.WriteLine("Result = derived called"); 
} 

public virtual void Method(BaseClass d) 
{ 
    Console.WriteLine("Result = base called"); 
} 

Method invokation nie jest dotknięte przez virtual słowo kluczowe w tej sprawie. Bez względu na to, czy oznaczone jest virtual, wywoływane jest najmniej pochodne przeciążenie. Tylko podczas override w klasie pochodnej zmienia się wywołanie metody.

Co zatem oznacza "typ uruchomień" i "typ kompilacji"? Jak wpływają na wywołanie metody?

+0

Proszę wyjaśnić, gdzie "Metoda" jest zadeklarowana i jak jest używana. –

+0

@BrianRasmussen - W OP te metody są członkami klasy pomocniczej. –

+1

Dzięki. W takim przypadku nie jestem pewien, w jaki sposób zaakceptowana odpowiedź pasuje do Twojego pytania, ponieważ nie zawiera klasy pomocnika. Jednak dopóki jesteś zadowolony z odpowiedzi, jestem dobry. –

Odpowiedz

5

Jest to bardziej kwestia metod wirtualnych niż nie-wirtualnych i sposobu wywołania. Część specyfikacji, którą cytujesz, dotyczy wywołań metod na zmiennej - wywołanie bcDerived.SomeMethod(), bez wywoływania foo.SomeMethod(bcDerived).

Specyfikacja jesteś powołując odnosi się do przypadku, gdy masz metody non-wirtualna:

public class A 
{ 
    public void Foo() { Console.WriteLine("A.Foo"); } 
    public virtual void Bar() { Console.WriteLine("A.Bar"); } 
} 
public class B : A 
{ 
    public new void Foo() { Console.WriteLine("B.Foo"); } 
    public override void Bar() { Console.WriteLine("B.Bar"); } 
} 

Następnie metoda zwana zostanie ustalona przez kompilator, w czasie kompilacji, tak robi:

A someInst = new B(); 
someInst.Foo(); 

spowoduje to nazwać A.Foo(), nieważne co podklasa A jest mowa o someInst, ponieważ to ja s jest metodą inną niż wirtualna.

Jeśli jednak masz metodę wirtualną, instrukcja callvirt jest określona przez kompilator, który przenosi decyzję do środowiska wykonawczego. Oznacza to, że:

someInst.Bar(); 

wezwie B.Bar(), nie A.Bar().

W twoim przypadku, nie jesteś wywołanie metody wirtualne (w tym sensie, że specyfikacja jest odnoszącym się do), ale robi standardowej rozdzielczości metody. 7.5.3 specyfikacji C# szczegółowo omawia rozdzielczość przeciążania. W twoim przypadku lista argumentów (bcDerived) jest sprawdzana przez kompilator i jest zdefiniowana jako typ BaseClass. "Najlepszym dopasowaniem" do tego będzie public virtual void Method(BaseClass d), ponieważ lista parametrów bezpośrednio odpowiada liście argumentów, więc jest używana w czasie kompilacji.

rozdzielczości przeciążenia metody, jeśli spojrzeć na specyfikację, nie bezpośrednio brać wirtualna metoda nazywa życie - tylko wygląda na niejawne konwersje między typami.

1

W tym przypadku typ kompilacji argumentu będzie zawsze używany do określenia, które przeciążenie należy wywołać. Wirtualna wysyłka zależy od typu środowiska wykonawczego obiektu, na który wywoływana jest metoda.

Typ czasu kompilacji to typ obiektu określony przez kompilator, a typ środowiska wykonawczego to rzeczywisty typ wykonywany przez kod. Aby użyć przykładu:

BaseClass bcDerived = new Derived() 

kompilacji typ czas jest BaseClass natomiast typ środowiska wykonawczego będzie Derived.

Aby zrozumieć konsekwencje Musimy rozszerzyć swoją klasę nieznacznie:

class BaseClass 
{ 
    public virtual void SomeMethod() 
    { 
    Console.WriteLine("In base class"); 
    } 
} 

class Derived : BaseClass 
{ 
    public override void SomeMethod() 
    { 
    Console.WriteLine("In derived class"); 
    } 
} 

Teraz nazywając bcDerived.SomeMethod() będzie zależeć od rodzaju środowiska wykonawczego bcDerived, czy realizacja BaseClass zostanie wywołana lub realizacja Derived zostanie wywołana .

Eric Lippert napisał bardzo dobrą serię trzyczęściową na wirtualnej wysyłce w .Net (z czego part one is here), zdecydowanie poleciłbym je przeczytać, aby w pełni zrozumieć temat.

0
Using these two classes as examples: 

public class Parent 
{ 
    public void NonVirtual() 
    { 
     Console.WriteLine("Nonvirtual - Parent"); 
    } 
    public virtual void Virtual() 
    { 
     Console.WriteLine("Virtual - Parent"); 
    } 
} 

public class Child : Parent 
{ 
    public override void Virtual() 
    { 
     Console.WriteLine("Virtual - Child"); 
    } 

    public void NonVirtual() 
    { 
     Console.WriteLine("Nonvirtual - Child"); 
    } 
} 

Różnica między wirtualnym a nie wirtualny jest najbardziej wyraźnie widoczne przez ten kod:

Parent childAsParent = new Child(); 
childAsParent.Virtual(); 
childAsParent.NonVirtual(); 

Drukuje:

Virtual - Child 
Nonvirtual - Parent

W przypadku metody wirtualne to widzi, w czasie wykonywania typ childAsParent jest podrzędny, a tym samym wykonuje definicję podrzędną Virtual. Dla metody nie-wirtualnej widzi on, że typ zmiennej czasu kompilacji to Parent i ignoruje fakt, że rzeczywistą instancją jest Child i wykorzystuje implementację rodzica.

virtual nie jest wykorzystywana do określenia, który sposób przeciążenie jest używany na podstawie typu parametrów. Określanie, które przeciążenie metody wywoływania jest zawsze wykonywane w czasie kompilacji (gdy nie jest używane dynamic), nigdy w czasie wykonywania, więc zawsze wybierze przeciążenie Method, w twoim przykładzie, na podstawie typu zmiennej czasu kompilacji .