Vă mulțumim pentru susținere

De ce nu pot avea metode statice în C #?

Am lucrat cu furnizorii un moment bun în ultimul timp și am venit într-o situație interesantă în care am vrut să am o clasă abstractă care avea o metodă statică abstractă. Am citit câteva postări pe această temă și a avut sens, dar există o explicație clară?

0
adăugat editat
Lăsați aceste lucruri deschise pentru a permite îmbunătățiri viitoare.
adăugat autor Mark Biek
În clasa de bază, metoda este nerezolvată și nu o puteți utiliza. Aveți nevoie fie de un tip derivat, fie de un obiect (care ar avea la rândul său un tip derivat). Ar trebui să puteți apela baseClassObject.Method () sau DerivedClass.Method (). Nu puteți apela BaseClass.Method () pentru că nu vă oferă tipul.
adăugat autor William Jockusch
Cred că se pune întrebarea că C # are nevoie de un alt cuvânt cheie, tocmai pentru acest tip de situație. Vrei o metodă a cărei valoare de retur depinde numai de tipul pe care se numește. Nu se poate numi "static" dacă tipul respectiv nu este cunoscut. Dar odată ce tipul devine cunoscut, va deveni static. Staticul nerezolvat este ideea - nu este încă statică, dar odată ce știm tipul de recepție, acesta va fi. Acesta este un concept perfect bun, motiv pentru care programatorii continuă să ceară acest lucru. Dar nu se potrivea perfect cu modul în care designerii se gândeau la limbaj.
adăugat autor William Jockusch
adăugat autor peterh
@WilliamJockusch ce înseamnă primirea de tip? Dacă numesc BaseClass.StaticMethod (), atunci BaseClass este singurul tip pe care îl poate utiliza pentru a lua decizia. Dar la acest nivel este abstract, astfel încât metoda nu poate fi rezolvată. Dacă numiți bine DerivedClass.StaticMethod, atunci clasa de bază este irelevantă.
adăugat autor Martin Capodici
Ca o notă laterală, TextWriter.Null și StreamWriter.Null au arătat o încercare similară în jurul acestui subiect.
adăugat autor Ken Kin

7 răspunsuri

Pentru a adăuga la explicațiile anterioare, apelurile metodice statice sunt legate de o anumită metodă la compile-time , ceea ce exclude mai degrabă comportamentul polimorf.

0
adăugat
Deci, exact cum credeți că polimorfismul funcționează pe CLR? Explicația dvs. a exclus exclusiv expedierea metodei virtuale.
adăugat autor Rytmis
Ne cerem scuze, nu am vrut să spun o insultă (deși recunosc că răspund un pic rapid ;-). Punctul întrebării mele a fost, dacă aveți aceste clase: class Base {public virtual void Method (); } class Derived: Base {public override void Metoda (); } și scrieți astfel: Exemplu de bază = nou derivat (); instance.Method (); informația despre tipul de timp de compilare pe site-ul de apel este că avem o instanță a Base, atunci când instanța reală este Derived. Deci, compilatorul nu poate rezolva metoda exactă de a apela. În schimb, acesta emite o instrucțiune IL "callvirt" IL care spune timpul de execu
adăugat autor Rytmis
... în timp ce pentru metodele statice nu există nici o instanță de a efectua trimiterea virtuală, deci emite mereu o instrucțiune de "apel". Și acum, când mi-am extins răspunsul, îmi dau seama cât de rău a fost răspunsul. ;-)
adăugat autor Rytmis
Mulțumesc omului, asta este informativ! Bănuiesc că l-am scos din picătură în IL destul de mult, dorește-mi noroc.
adăugat autor Adam Tolley
Acesta nu este un comentariu la fel de util ca ar putea fi. Am invitat (cu "așa cum am înțeles") discurs util, cred că ați putea oferi un conținut mai mic - văzând că oamenii vin aici căutând răspunsuri și nu insulte. Deși, se pare că s-ar putea să fiu vinovat de același lucru - chiar m-am referit la comentariul de mai sus ca o întrebare: C # nu evaluează aceste lucruri la momentul compilării?
adăugat autor Adam Tolley
C # este tipărit static; apelurile la metode polimorfe sunt de asemenea legate la timpul de compilare așa cum o înțeleg - adică CLR nu este lăsat să rezolve ce metodă să apelezi în timpul runtime-ului.
adăugat autor Adam Tolley

Metodele statice nu sunt instanțiate ca atare, sunt disponibile doar fără referință de obiect.

Un apel la o metodă statică se face prin numele clasei, nu printr-o referință de obiect, iar codul IL de apelare va numi metoda abstractă prin numele clasei care a definit-o, nu neapărat numele clasei pe care ați folosit-o .

Permiteți-mi să arăt un exemplu.

Cu următorul cod:

public class A
{
    public static void Test()
    {
    }
}

public class B : A
{
}

Dacă chemați B.Test, după cum urmează:

class Program
{
    static void Main(string[] args)
    {
        B.Test();
    }
}

Apoi, codul real din interiorul metodei principale este după cum urmează:

.entrypoint
.maxstack 8
L0000: nop 
L0001: call void ConsoleApplication1.A::Test()
L0006: nop 
L0007: ret 

După cum puteți vedea, apelul este trimis către A.Test, deoarece a fost clasa A care a definit-o și nu lui B.Test, chiar dacă puteți scrie codul în acest fel.

Dacă ați avea tipuri de clase , cum ar fi în Delphi, unde puteți face o variabilă care se referă la un tip și nu la un obiect, ați avea mai multă utilizare pentru metode statice virtuale și astfel abstracte (și constructori) dar acestea nu sunt disponibile și, prin urmare, apelurile statice sunt non-virtuale în .NET.

Îmi dau seama că designerii IL ar putea permite compilarea codului pentru a apela B.Test și a rezolva apelul în timpul rulării, dar încă nu ar fi virtual, așa cum ar trebui să scrie un fel de clasă acolo.

Metodele virtuale și, prin urmare, cele abstracte sunt utile numai atunci când utilizați o variabilă care, la timpul de execuție, poate conține mai multe tipuri diferite de obiecte și, prin urmare, doriți să apelați metoda potrivită pentru obiectul curent pe care îl aveți în variabilă. Cu metode statice trebuie să treci oricum printr-un nume de clasă, astfel încât metoda exactă de a apela este cunoscută la momentul compilării deoarece nu se poate și nu se va schimba.

Astfel, metodele statice virtuale / abstracte nu sunt disponibile în .NET.

0
adăugat
Parametrii de tip generic se comportă efectiv ca variabile non-persistente, iar metodele statice virtuale pot fi utile în acest context. De exemplu, dacă ați avea un tip Car cu o metodă statică CreateFromDescription statică virtuală, atunci codul care a acceptat un tip generic > T ar putea apela T.CreateFromDescription pentru a produce o mașină de tipul T . O astfel de construcție ar putea fi susținută destul de bine în cadrul CLR dacă fiecare tip care a definit o astfel de metodă a avut o instanță statică singleton a un
adăugat autor supercat
În combinație cu modul în care operatorul suprasolicite se face în C #, acest lucru, din nefericire, elimină posibilitatea de a solicita subclaselor să ofere o implementare pentru o supraîncărcare dată unui operator.
adăugat autor Chris Moschini

De fapt, depășim metodele statice (în Delphi), este puțin urât, dar funcționează bine pentru nevoile noastre.

Îl folosim astfel încât clasele să poată avea o listă a obiectelor lor disponibile fără instanța clasei, de exemplu, avem o metodă care arată astfel:

class function AvailableObjects: string; override;
begin
  Result := 'Object1, Object2';
end; 

Este urât, dar necesar, astfel putem instantiza exact ceea ce este necesar, în loc să avem toate instanțele instanțiate doar pentru a căuta obiectele disponibile.

Acesta a fost un exemplu simplu, dar aplicația în sine este o aplicație client-server care are toate clasele disponibile pe un singur server și mai mulți clienți diferiți care nu au nevoie de tot ce are serverul și nu va avea nevoie de o instanță de obiect.

Deci, acest lucru este mult mai ușor de întreținut decât de a avea o aplicație server diferită pentru fiecare client.

Sper că exemplul a fost clar.

0
adăugat

Un alt respondent (McDowell) a spus că polimorfismul funcționează numai pentru instanțe de obiecte. Aceasta ar trebui să fie calificată; există limbi care tratează clasele ca exemple de tip "Class" sau "Metaclass". Aceste limbi suportă polimorfismul pentru metodele de exemplu și de clasă (statice).

C #, cum ar fi Java și C ++ înaintea lui, nu este o astfel de limbă; cuvântul cheie static este folosit în mod explicit pentru a indica faptul că metoda este legată static mai degrabă decât dinamică / virtuală.

0
adăugat

Metodele statice nu pot fi moștenite sau suprasolicitate și de aceea nu pot fi abstracte. Deoarece metodele statice sunt definite pe tip, nu în instanță, dintr-o clasă, ele trebuie să fie numite în mod explicit pe acel tip. Deci, atunci când doriți să apelați o metodă pe o clasă de copii, trebuie să utilizați numele acesteia pentru a o apela. Aceasta face ca moștenirea să fie irelevantă.

Să presupunem că ai putea, pentru un moment, să moștenim metode statice. Imaginați-vă acest scenariu:

public static class Base
{
    public static virtual int GetNumber() { return 5; }
}

public static class Child1 : Base
{
    public static override int GetNumber() { return 1; }
}

public static class Child2 : Base
{
    public static override int GetNumber() { return 2; }
}

Dacă apelați Base.GetNumber (), care metodă ar fi apelată? Ce valoare a revenit? Este ușor de văzut că, fără a crea instanțe de obiecte, moștenirea este destul de greu. Metodele abstract fără moștenire sunt doar metode care nu au un corp, deci nu pot fi chemați.

0
adăugat
@ArtemRussakovskii: Să presupunem că a avut int DoSomething () unde T: Base {return T.GetNumber ();} . S-ar părea util dacă DoSomething () ar putea reveni la cinci, în timp ce DoSomething () . O astfel de abilitate ar fi utilă nu numai pentru exemplele de jucării, ci și pentru o clasă Car {public Static Virtual Build (PO PurchaseOrder);} , unde fiecare clasă derivă din Car ar trebui să definească o metodă care ar putea construi o instanță în urma unei comenzi de achiziție.
adăugat autor supercat
De ce în lume, Base.GetNumber () va returna altceva decât 5? Este o metodă în clasa de bază - există doar o opțiune acolo.
adăugat autor Artem Russakovskii
Există exact aceeași "problemă" cu moștenirea non-statică.
adăugat autor Ark-kun
Având în vedere scenariul dvs. aș spune Base.GetNumber () ar reveni 5; Child1.GetNumber () returnează 1; Child2.GetNumber () returnează 2; Mă poți dovedi greșit, să mă ajuți să-ți înțeleg raționamentul? Mulțumesc
adăugat autor Luis Filipe
Fața pe care o credeți că Base.GetNumber () returnează 5, înseamnă că deja înțelegeți ce se întâmplă. Prin returnarea valorii de bază, nu există nicio moștenire.
adăugat autor David Wengier

Metodele abstracte sunt implicit virtuale. Metodele abstract necesită o instanță, dar metodele statice nu au o instanță. Deci, puteți avea o metodă statică într-o clasă abstractă, ea nu poate fi abstractă statică (sau statică abstractă).

0
adăugat
-1 metode virtuale nu au nevoie de o instanță, cu excepția desenului. Și nu vă adresați cu adevărat întrebării, atât de mult încât o deflectați.
adăugat autor TJMonk15

Iată o situație în care există cu certitudine o necesitate de moștenire pentru câmpurile și metodele statice:

abstract class Animal
{
  protected static string[] legs;

  static Animal() {
    legs=new string[0];
  }

  public static void printLegs()
  {
    foreach (string leg in legs) {
      print(leg);
    }
  }
}


class Human: Animal
{
  static Human() {
    legs=new string[] {"left leg", "right leg"};
  }
}


class Dog: Animal
{
  static Dog() {
    legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"};
  }
}


public static void main() {
  Dog.printLegs();
  Human.printLegs();
}


//what is the output?
//does each subclass get its own copy of the array "legs"?
0
adăugat
Nu, există doar o singură instanță a "picioarelor" matricei. Rezultatul este nedeterminist, deoarece nu știți ce ordine vor fi numiți constructorii statici (de fapt nu există nici o garanție că constructorul static de bază ar fi apelat deloc). "Nevoia" este un termen destul de absolut, unde "dorința" este probabil mai exactă.
adăugat autor Sam
legs ar trebui să fie o proprietate abstractă statică.
adăugat autor AbleArcher
Ei bine, nu mă forțează să chem un constructor static. Dar o să o iau acum ...
adăugat autor Evren Ozturk