Moje pytanie dotyczy charakterystyki wydajności metod statycznych względem metod instancyjnych oraz ich skalowalności. Załóżmy, że w tym scenariuszu wszystkie definicje klas znajdują się w jednym zestawie i wymagane jest wiele dyskretnych typów wskaźników.
Rozważać:
public sealed class InstanceClass
{
public int DoOperation1(string input)
{
// Some operation.
}
public int DoOperation2(string input)
{
// Some operation.
}
// … more instance methods.
}
public static class StaticClass
{
public static int DoOperation1(string input)
{
// Some operation.
}
public static int DoOperation2(string input)
{
// Some operation.
}
// … more static methods.
}
Powyższe klasy reprezentują wzorzec stylu pomocnika.
W klasie instancji rozwiązanie metody instancji zajmuje chwilę, w przeciwieństwie do metody StaticClass.
Moje pytania to:
Kiedy zachowanie stanu nie jest problemem (nie są wymagane żadne pola ani właściwości), czy zawsze lepiej jest używać klasy statycznej?
Jeśli istnieje znaczna liczba tych statycznych definicji klas (na przykład 100, z których każda zawiera wiele metod statycznych), czy wpłynie to negatywnie na wydajność wykonywania lub zużycie pamięci w porównaniu z taką samą liczbą definicji klas instancji?
Gdy wywoływana jest inna metoda w tej samej klasie instancji, czy rozpoznawanie instancji nadal występuje? Na przykład użycie słowa kluczowego [this] jak
this.DoOperation2("abc")
zDoOperation1
tego samego wystąpienia.
źródło
this
coś wskazuje na coś, gdy klasa wywołuje metodę instancji na sobie?”.Odpowiedzi:
Teoretycznie metoda statyczna powinna działać nieco lepiej niż metoda instancji, przy czym wszystkie inne
this
parametry powinny być takie same, ze względu na dodatkowy ukryty parametr.W praktyce robi to tak małą różnicę, że będzie ukryte w szumie różnych decyzji kompilatora. (W związku z tym dwie osoby mogą „udowodnić” jedną lepiej niż drugą z różnymi wynikami). Nie tylko dlatego, że
this
jest zwykle przekazywany w rejestrze i często znajduje się w tym rejestrze na początku.Ten ostatni punkt oznacza, że teoretycznie powinniśmy spodziewać się statycznej metody, która przyjmuje obiekt jako parametr i robi coś z nim, będzie nieco gorsza niż odpowiednik jako instancja tego samego obiektu. Znowu jednak różnica jest tak niewielka, że gdybyś próbował ją zmierzyć, prawdopodobnie skończyłbyś mierzeniem innej decyzji kompilatora. (Zwłaszcza, że prawdopodobieństwo, że odniesienie to będzie w rejestrze przez cały czas, jest również dość wysokie).
Rzeczywiste różnice w wydajności sprowadzą się do tego, czy sztucznie masz obiekty w pamięci, aby zrobić coś, co naturalnie powinno być statyczne, czy też splątasz łańcuchy przekazywania obiektów w skomplikowany sposób, aby zrobić to, co powinno naturalnie być instancją.
Stąd dla numeru 1. Kiedy utrzymywanie stanu nie jest problemem, zawsze lepiej być statycznym, ponieważ statystyka służy temu . Nie jest to problem z wydajnością, chociaż istnieje ogólna zasada ładnego grania z optymalizacjami kompilatora - jest bardziej prawdopodobne, że ktoś zadał sobie trud optymalizacji przypadków, które pojawiają się przy normalnym użyciu, niż tych, które wymyślają dziwne użycie.
Numer 2. Nie ma różnicy. Dla każdego członka istnieje pewna kwota kosztu na klasę, która zależy zarówno od ilości metadanych, ile kodu znajduje się w rzeczywistym pliku DLL lub EXE, jak i od tego, ile będzie w nim utraconego kodu. To jest to samo, czy jest to instancja, czy statyczna.
W przypadku pozycji 3
this
jest takthis
samo. Jednak uwaga:this
Parametr jest przekazywana w danym rejestrze. Podczas wywoływania metody instancji w tej samej klasie, prawdopodobnie będzie ona już w tym rejestrze (chyba że została ukryta i rejestr używany z jakiegoś powodu), a zatem nie jest wymagana żadna akcja, aby ustawićthis
to, co należy ustawić . Dotyczy to w pewnym stopniu np. Pierwszych dwóch parametrów metody, która jest pierwszymi dwoma parametrami wywołania, które wykonuje.Ponieważ będzie jasne, że
this
nie jest to null, w niektórych przypadkach może to służyć do optymalizacji połączeń.Ponieważ będzie jasne, że
this
nie jest null, może to ponownie zwiększyć wydajność wywołań metod wbudowanych, ponieważ kod utworzony w celu sfałszowania wywołania metody może pominąć niektóre sprawdzenia wartości null, których i tak może potrzebować.To powiedziawszy, czeki zerowe są tanie!
Warto zauważyć, że ogólne metody statyczne działające na obiekcie, a nie metody instancji, mogą zmniejszyć niektóre koszty omówione na stronie http://joeduffyblog.com/2011/10/23/on-generics-and-some-of- związane z narzutami / w przypadku, gdy dana statyczna nie jest wywoływana dla danego typu. Jak to ujął: „Na marginesie okazuje się, że metody rozszerzające to świetny sposób, aby generyczne abstrakcje były bardziej opłacalne”.
Należy jednak pamiętać, że dotyczy to tylko tworzenia instancji innych typów używanych przez metodę, które w innym przypadku nie istnieją. W związku z tym tak naprawdę nie ma zastosowania w wielu przypadkach (inna metoda instancji używała tego typu, jakiś inny kod w innym miejscu używał tego typu).
Podsumowanie:
Edycja: uwaga na temat tego, jak tanie są czeki zerowe (które twierdziłem powyżej). Większość sprawdzeń null w .NET w ogóle nie sprawdza wartości null, raczej kontynuuje to, co zamierzali zrobić, zakładając, że zadziała, a jeśli zdarzy się wyjątek dostępu, zostanie zamieniony w plik
NullReferenceException
. W związku z tym, głównie wtedy, gdy koncepcyjnie kod C # obejmuje sprawdzenie wartości null, ponieważ uzyskuje dostęp do elementu członkowskiego wystąpienia, koszt, jeśli się powiedzie, wynosi w rzeczywistości zero. Wyjątkiem byłyby niektóre wbudowane wywołania (ponieważ chcą zachowywać się tak, jakby dzwonili do członka instancji) i po prostu uderzają w pole, aby wywołać to samo zachowanie, więc są również bardzo tanie i i tak często można je pominąć (np. jeśli pierwszy krok metody obejmował dostęp do pola w takiej postaci).źródło
this
lub przez jawny parametr. Większy wpływ miałby tutaj to, jak blisko danych są powiązane dane (pola typu wartości lub wartości tablic są bliżej niż dane w polach typu referencyjnego) oraz wzorce dostępu.obj.DoSomehting(2)
że byłaby nieco tańsza niż,DoSomething(obj, 2)
ale jak powiedziałem również, różnica jest tak niewielka i tak zależna od drobnych rzeczy, które mogą skończyć się inaczej w ostatecznej kompilacji, że nie warto się o to martwić. Jeśli robisz coś tak kosztownego (w stosunku do tego rodzaju różnic w grze) jak serializowanie czegoś do łańcucha, to jest to szczególnie bezcelowe.ctor
nadal wymaga inicjalizacji wszystkich pól. Jeśli masz już instancję, obowiązuje ta odpowiedź („wszystkie inne rzeczy są równe”). Oczywiście kosztownecctor
może również spowolnić metody statyczne, ale dzieje się tak tylko przy pierwszym wywołaniu i dotyczy to jednakowo metod instancji. Zobacz także docs.microsoft.com/en-us/previous-versions/dotnet/articles/…Powiedziałbym tak. Deklarując coś
static
, deklarujesz zamiar bezpaństwowej egzekucji (nie jest to obowiązkowe, ale zamiar czegoś, czego można by się spodziewać)Nie myśl tak, chyba że jesteś pewien, że klasy statyczne są naprawdę bezpretensjonalne, ponieważ jeśli nie, łatwo zepsuć alokacje pamięci i uzyskać wycieki pamięci.
Nie jestem pewien, co do tego punktu (jest to czysto implementacyjny szczegół CLR), ale myślę, że tak.
źródło
metody statyczne są szybsze, ale mniej OOP, jeśli będziesz używać wzorców projektowych metoda statyczna prawdopodobnie zły kod, aby lepiej pisać logikę biznesową bez statycznej, typowe funkcje, takie jak czytanie plików, żądanie WebRequest itp. lepiej realizować się jako statyczne ... Twoje pytania nie mają uniwersalnego odpowiedź
źródło