Do czego służy typ „dynamiczny” w C # 4.0?

236

W C # 4.0 wprowadzono nowy typ o nazwie „dynamiczny”. Wszystko brzmi dobrze, ale do czego programista by to wykorzystał?

Czy jest sytuacja, w której może uratować dzień?

Fahad
źródło
4
możliwy duplikat stackoverflow.com/questions/2255982/...
Jörg W Mittag
Przydaje się to podczas pracy z COM lub dynamicznie pisanymi językami. Na przykład, jeśli używasz lua lub python do pisania skryptów w swoim języku, bardzo wygodnie jest po prostu wywołać kod skryptowy, tak jakby był to normalny kod.
CodesInChaos
Mam nadzieję, że ten artykuł ma pełną odpowiedź na twoje pytanie visualstudiomagazine.com/Articles/2011/02/01/…
Deweloper

Odpowiedzi:

196

Dynamiczne słowo kluczowe jest nowością w C # 4.0 i służy do informowania kompilatora, że ​​typ zmiennej może się zmienić lub że nie jest znany do czasu uruchomienia. Pomyśl o tym, że może wchodzić w interakcje z przedmiotem bez rzucania go.

dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!

Uwaga: nie musieliśmy przesyłać ani deklarować klienta jako klienta typu. Ponieważ zadeklarowaliśmy, że jest dynamiczny, środowisko wykonawcze przejmuje, a następnie wyszukuje i ustawia dla nas właściwość FirstName. Teraz, oczywiście, kiedy używasz zmiennej dynamicznej, rezygnujesz z sprawdzania typu kompilatora. Oznacza to, że wywołanie cust.MissingMethod () zostanie skompilowane i nie zakończy się niepowodzeniem, dopóki nie zostanie uruchomione. Wynikiem tej operacji jest RuntimeBinderException, ponieważ MissingMethod nie jest zdefiniowany w klasie Customer.

Powyższy przykład pokazuje, jak działa dynamika podczas wywoływania metod i właściwości. Kolejną potężną (i potencjalnie niebezpieczną) funkcją jest możliwość ponownego wykorzystywania zmiennych dla różnych typów danych. Jestem pewien, że programiści Python, Ruby i Perl mogą wymyślić milion sposobów na skorzystanie z tego, ale używam C # tak długo, że wydaje mi się to „złe”.

dynamic foo = 123;
foo = "bar";

OK, więc najprawdopodobniej nie będziesz pisać kodu tak jak powyżej. Może się jednak zdarzyć, że ponowne użycie zmiennych może się przydać lub wyczyścić brudny kawałek starszego kodu. Jednym prostym przypadkiem, w którym często się spotykam, jest ciągłe rzucanie między dziesiętną a podwójną.

decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");

Druga linia nie jest kompilowana, ponieważ 2.5 jest wpisany jako podwójny, a linia 3 nie jest kompilowana, ponieważ Math.Sqrt oczekuje podwójnej. Oczywiście wszystko, co musisz zrobić, to rzutować i / lub zmienić typ zmiennej, ale mogą zdarzyć się sytuacje, w których użycie dynamiki ma sens.

dynamic foo = GetDecimalValue(); // still returns a decimal
foo = foo / 2.5; // The runtime takes care of this for us
foo = Math.Sqrt(foo); // Again, the DLR works its magic
string bar = foo.ToString("c");

Czytaj więcej funkcji: http://www.codeproject.com/KB/cs/CSharp4Features.aspx

Pranay Rana
źródło
96
Osobiście nie podoba mi się myśl użycia dynamicin c # do rozwiązywania problemów, które można rozwiązać (być może nawet lepiej) za pomocą standardowych funkcji c # i pisania statycznego lub co najwyżej za pomocą wnioskowania typu ( var). dynamicpowinien być stosowany tylko w przypadku problemów z interoperacyjnością z DLR. Jeśli piszesz kod w języku statycznym, takim jak c #, zrób to i nie emuluj języka dynamicznego. To po prostu brzydkie.
Philip Daubmeier
40
Jeśli często używasz dynamiczmiennych w swoim kodzie, gdzie ich nie potrzebujesz (jak w przykładzie z kwadratowym korzeniem), rezygnujesz z sprawdzania błędów podczas kompilacji; zamiast tego otrzymujesz możliwe błędy czasu wykonywania.
Philip Daubmeier
33
W większości w porządku, ale kilka drobnych błędów. Po pierwsze, nie jest poprawne stwierdzenie, że dynamiczny oznacza, że typ zmiennej może się zmienić. Ta zmienna jest typu „dynamiczna” (z perspektywy języka C #; z perspektywy CLR zmienna jest typu obiektowego). Typ zmiennej nigdy się nie zmienia. Typem wykonawczym wartości zmiennej może być dowolny typ zgodny z typem zmiennej. (Lub w przypadku typów referencyjnych może być zerowy.)
Eric Lippert
15
Jeśli chodzi o twój drugi punkt: C # miał już funkcję „stwórz zmienną, w którą możesz umieścić wszystko” - zawsze możesz zrobić zmienną typu obiektowego. Interesującą rzeczą dotyczącą dynamiki jest to, na co zwracasz uwagę w pierwszym akapicie: dynamika jest prawie identyczna z obiektem, z wyjątkiem tego, że analiza semantyczna jest odraczana do czasu wykonania, a analiza semantyczna jest wykonywana na typie wyrażenia w czasie wykonywania. (Przeważnie. Istnieją pewne wyjątki.)
Eric Lippert
18
Wydałem na to negatywny punkt, głównie dlatego, że domyślnie zaleca użycie słowa kluczowego do ogólnego użytku. Ma konkretnie ukierunkowany cel (dokładnie opisany w odpowiedzi Lassesa) i chociaż ta odpowiedź jest technicznie poprawna, prawdopodobnie sprowadzi deweloperów na manowce.
Ośmiobitowy Guru,
211

Słowo dynamickluczowe zostało dodane wraz z wieloma innymi nowymi funkcjami C # 4.0, aby ułatwić rozmowę z kodem, który mieszka lub pochodzi z innych środowisk wykonawczych, które mają różne interfejsy API.

Brać przykład.

Jeśli masz obiekt COM, taki jak Word.Applicationobiekt, i chcesz otworzyć dokument, metoda wykonania tego ma co najmniej 15 parametrów, z których większość jest opcjonalna.

Aby wywołać tę metodę, potrzebujesz czegoś takiego (upraszczam, to nie jest rzeczywisty kod):

object missing = System.Reflection.Missing.Value;
object fileName = "C:\\test.docx";
object readOnly = true;
wordApplication.Documents.Open(ref fileName, ref missing, ref readOnly,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing, ref missing, ref missing, ref missing,
    ref missing, ref missing);

Zwróć uwagę na te wszystkie argumenty? Musisz przekazać je, ponieważ C # przed wersją 4.0 nie zawierał opcjonalnych argumentów. W C # 4.0 interfejsy API COM zostały ułatwione dzięki wprowadzeniu:

  1. Opcjonalne argumenty
  2. Dokonywanie refopcjonalne dla COM API
  3. Nazwane argumenty

Nowa składnia powyższego wywołania to:

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

Zobacz, o ile łatwiej to wygląda, o ile bardziej staje się czytelne?

Rozbijmy to na części:

                                    named argument, can skip the rest
                                                   |
                                                   v
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
                                 ^                         ^
                                 |                         |
                               notice no ref keyword, can pass
                               actual parameter values instead

Magia polega na tym, że kompilator C # wstrzykuje teraz niezbędny kod i pracuje z nowymi klasami w środowisku wykonawczym, aby wykonać prawie dokładnie to samo co wcześniej, ale składnia została przed tobą ukryta, teraz możesz skupić się na co , a nie tak bardzo jak . Anders Hejlsberg lubi mówić, że trzeba przywoływać różne „zaklęcia”, co jest swoistą kalamburą całej magii, w której zazwyczaj trzeba machać ręką (rękami) i wypowiadać magiczne słowa we właściwej kolejności aby uzyskać pewien rodzaj zaklęcia. Stary sposób API do komunikowania się z obiektami COM był bardzo duży, trzeba było przeskakiwać przez wiele obręczy, aby nakłonić kompilator do skompilowania kodu za ciebie.

Sprawy psują się w C # przed wersją 4.0, a nawet bardziej, jeśli próbujesz porozmawiać z obiektem COM, dla którego nie masz interfejsu lub klasy, wszystko, co masz, to IDispatchodwołanie.

Jeśli nie wiesz, co to jest, IDispatchjest w zasadzie odbiciem dla obiektów COM. Za pomocą IDispatchinterfejsu możesz zapytać obiekt „jaki jest numer identyfikacyjny metody znanej jako Save”, i zbudować tablice określonego typu zawierające wartości argumentów, a na końcu wywołać Invokemetodę IDispatchinterfejsu, aby wywołać metodę, przekazując wszystkie informacje, które udało ci się zebrać razem.

Powyższa metoda Save może wyglądać następująco (to zdecydowanie nie jest właściwy kod):

string[] methodNames = new[] { "Open" };
Guid IID = ...
int methodId = wordApplication.GetIDsOfNames(IID, methodNames, methodNames.Length, lcid, dispid);
SafeArray args = new SafeArray(new[] { fileName, missing, missing, .... });
wordApplication.Invoke(methodId, ... args, ...);

Wszystko po to, by otworzyć dokument.

VB miał opcjonalne argumenty i wsparcie dla większości tego od razu po wyjęciu z pudełka, więc ten kod C #:

wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);

jest w zasadzie po prostu C # dogania VB pod względem ekspresyjności, ale robi to we właściwy sposób, umożliwiając rozszerzenie, nie tylko dla COM. Oczywiście jest to również dostępne dla VB.NET lub dowolnego innego języka zbudowanego na środowisku uruchomieniowym .NET.

Więcej informacji na temat IDispatchinterfejsu można znaleźć na Wikipedii: IDispatch, jeśli chcesz dowiedzieć się więcej na ten temat. To naprawdę krwawe rzeczy.

Co jednak, jeśli chcesz porozmawiać z obiektem Python? Istnieje inny interfejs API niż ten używany dla obiektów COM, a ponieważ obiekty Pythona również mają charakter dynamiczny, musisz skorzystać z magii odbicia, aby znaleźć odpowiednie metody wywoływania, ich parametry itp., Ale nie .NET odbicie, coś napisanego dla Pythona, zupełnie jak powyższy kod IDispatch, zupełnie inny.

A dla Ruby? Wciąż inny interfejs API.

JavaScript? Ta sama oferta, inne API również.

Dynamiczne słowo kluczowe składa się z dwóch rzeczy:

  1. Nowe słowo kluczowe w C #, dynamic
  2. Zestaw klas środowiska wykonawczego, które wiedzą, jak radzić sobie z różnymi typami obiektów, które implementują określony interfejs API dynamicwymagany przez słowo kluczowe, i odwzorowuje wywołania we właściwy sposób. Interfejs API jest nawet udokumentowany, więc jeśli masz obiekty pochodzące z środowiska wykonawczego, które nie są objęte, możesz je dodać.

Słowo dynamickluczowe nie ma jednak na celu zastąpienia żadnego istniejącego kodu tylko .NET. Jasne, możesz to zrobić, ale z tego powodu nie został dodany, a autorzy języka programowania C # z Anders Hejlsberg na czele, byli bardzo nieugięci, że nadal uważają C # za język mocno napisany na maszynie i nie poświęcą się ta zasada.

Oznacza to, że chociaż możesz napisać taki kod:

dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;

i niech to się skompiluje, nie miało to być rodzajem systemu typu magia, który wymyślił, co miałeś na myśli w czasie wykonywania.

Głównym celem było ułatwienie rozmowy z innymi typami obiektów.

W Internecie znajduje się mnóstwo materiałów na temat słowa kluczowego, zwolenników, przeciwników, dyskusji, rantów, pochwał itp.

Sugeruję zacząć od poniższych linków, a następnie google, aby uzyskać więcej:

Lasse V. Karlsen
źródło
12
Jest także przydatny oprócz COM dla internetowych interfejsów API JSON, w których struktura zdserializowanych obiektów JSON nie jest określona w języku C #. Na przykład metoda dekodowania System.Web.Helpers.Json zwraca obiekt dynamiczny .
dumbledad
Pomijając to, że „nadal uważają C # za język mocno napisany na maszynie”: Eric Lippert nie jest fanem „silnie napisanego” jako opisu.
Andrew Keeton,
Nie zgadzam się z nim, ale to kwestia opinii, a nie faktów. „Silnie wpisany” oznacza dla mnie, że kompilator wie w czasie kompilacji, który typ jest używany, a tym samym egzekwuje reguły ustawione wokół tych typów. Fakt, że możesz wybrać typ dynamiczny, który odracza sprawdzanie reguł i wiązanie do środowiska wykonawczego, nie oznacza dla mnie, że język jest słabo wpisany. Zwykle nie kontrastuję silnie pisanego z słabo pisanym, jednak zwykle porównuję go do pisania dynamicznego, takiego jak języki takie jak Python, gdzie wszystko jest kaczką, dopóki nie zaszczeka.
Lasse V. Karlsen
Jaki jest sens tej odpowiedzi? Połowa z nich dotyczy parametrów opcjonalnych i interfejsu IDispatch.
Xam
Właśnie dlatego dynamicdodano, aby wesprzeć inne ekosystemy w tym, jak można wywoływać metody refleksyjne, a także zapewnić rodzaj czarnej skrzynki dla struktur danych z udokumentowanym sposobem osiągnięcia tego.
Lasse V. Karlsen
29

Dziwi mnie, że nikt nie wspomniał o wysyłce wielokrotnej . Zwykłym sposobem obejścia tego problemu jest wzorzec użytkownika i nie zawsze jest to możliwe, więc kończy się na stosach isczeków.

Oto przykład mojego własnego zastosowania. Zamiast robić:

public static MapDtoBase CreateDto(ChartItem item)
{
    if (item is ElevationPoint) return CreateDtoImpl((ElevationPoint)item);
    if (item is MapPoint) return CreateDtoImpl((MapPoint)item);
    if (item is MapPolyline) return CreateDtoImpl((MapPolyline)item);
    //other subtypes follow
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

Ty robisz:

public static MapDtoBase CreateDto(ChartItem item)
{
    return CreateDtoImpl(item as dynamic);
}

private static MapDtoBase CreateDtoImpl(ChartItem item)
{
    throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}

private static MapDtoBase CreateDtoImpl(MapPoint item)
{
    return new MapPointDto(item);
}

private static MapDtoBase CreateDtoImpl(ElevationPoint item)
{
    return new ElevationDto(item);
}

Zauważ, że w pierwszym przypadku ElevationPointjest ona podklasą MapPointi jeśli nie zostanie umieszczona wcześniej MapPoint , nigdy nie zostanie osiągnięta. Nie dotyczy to dynamiki, ponieważ zostanie wywołana metoda najbliższego dopasowania.

Jak można się domyślić na podstawie kodu, ta funkcja przydała się, gdy wykonywałem tłumaczenie obiektów ChartItem na ich wersje możliwe do serializacji. Nie chciałem zanieczyszczać mojego kodu odwiedzającymi i nie chciałem również zanieczyszczać mojegoChartItem obiektów bezużytecznymi atrybutami specyficznymi dla serializacji.

Stelios Adamantidis
źródło
Nie wiedziałem o tym przypadku użycia. W najlepszym razie trochę zuchwały. Zrzuci każdy analizator statyczny.
Kugel
2
@Kugel to prawda, ale nie nazwałbym tego hackem . Analiza statyczna jest dobra, ale nie pozwoliłbym, aby powstrzymała mnie od eleganckiego rozwiązania, w którym alternatywami są: naruszenie zasady otwartego zamknięcia (wzorzec odwiedzin) lub zwiększona złożoność cykliczna z przerażającym isstosem jeden na drugim.
Stelios Adamantidis
4
Masz opcję dopasowania wzoru do C # 7, nie?
Kugel
2
Cóż, operatory są w ten sposób o wiele tańsze (unikając podwójnego rzutowania) i otrzymujesz z powrotem analizę statyczną ;-) i wydajność.
Kugel
@idbrii proszę nie zmieniaj moich odpowiedzi. Dodaj komentarz, a wyjaśnię (w razie potrzeby), ponieważ nadal jestem aktywny w tej społeczności. Proszę również nie używać magic; nie ma czegoś takiego jak magia.
Stelios Adamantidis,
11

Ułatwia współdziałanie języków statycznych (CLR) z dynamicznymi (python, ruby ​​...) działającymi na DLR (środowisko uruchomieniowe języka dynamicznego), patrz MSDN :

Na przykład możesz użyć następującego kodu, aby zwiększyć licznik w XML w C #.

Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);

Korzystając z DLR, możesz użyć następującego kodu zamiast do tej samej operacji.

scriptobj.Count += 1;

MSDN wymienia następujące zalety:

  • Upraszcza przenoszenie języków dynamicznych do .NET Framework
  • Włącza funkcje dynamiczne w językach o typie statycznym
  • Zapewnia przyszłe korzyści z DLR i .NET Framework
  • Umożliwia udostępnianie bibliotek i obiektów
  • Zapewnia szybką dynamiczną wysyłkę i wywołanie

Aby uzyskać więcej informacji, zobacz MSDN .

Philip Daubmeier
źródło
1
A zmiana wymagana przez VM dla dynamiki faktycznie ułatwia dynamiczne języki.
Dykam
2
@Dykam: Nie ma zmian w maszynie wirtualnej. DLR działa dobrze aż do .NET 2.0.
Jörg W Mittag
@ Jörg, tak, jest zmiana. DLR jest częściowo przepisany, ponieważ teraz maszyna wirtualna ma wbudowaną obsługę dynamicznego rozwiązywania problemów.
Dykam
Byłem trochę zbyt optymistyczny, badania wykazały, że zmiany nie były tak duże.
Dykam
4

Przykład zastosowania:

Zużywasz wiele klas, które mają właściwość komunalną „CreationDate”:

public class Contact
{
    // some properties

    public DateTime CreationDate { get; set; }        
}

public class Company
{
    // some properties

    public DateTime CreationDate { get; set; }

}

public class Opportunity
{
    // some properties

    public DateTime CreationDate { get; set; }

}

Jeśli napiszesz metodę commun, która pobiera wartość właściwości „CreationDate”, będziesz musiał użyć refleksji:

    static DateTime RetrieveValueOfCreationDate(Object item)
    {
        return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
    }

Dzięki koncepcji „dynamicznej” kod jest znacznie bardziej elegancki:

    static DateTime RetrieveValueOfCreationDate(dynamic item)
    {
        return item.CreationDate;
    }
Akli
źródło
7
Pisanie kaczek, miło. Jednak powinieneś użyć do tego interfejsu, jeśli są to twoje typy.
Kugel
3

COM interop. Szczególnie Nieznany. Został zaprojektowany specjalnie do tego.

Aen Sidhe
źródło
2

Będzie głównie używany przez ofiary RAD i Pythona do niszczenia jakości kodu, IntelliSense i wykrywania błędów kompilacji czasu.

stormianrootsolver
źródło
Cyniczna odpowiedź, ale łatwo zbyt prawdziwa. Widziałem to po prostu w celu uniknięcia deklarowania struktur, w wyniku czego kod działa, jeśli wszystko jest w porządku, ale wysadza stos w nieprzewidywalny sposób, gdy tylko przeniesiesz swój ser.
AnthonyVO
Tak, zobaczysz to klasyczne cięcie narożne z wieloma innymi funkcjami językowymi. Nic dziwnego, że zobaczysz to tutaj.
Hawkeye4040,
1

Sprawdza się w czasie wykonywania, więc możesz zmienić typ, tak jak w JavaScript, na cokolwiek chcesz. Jest to uzasadnione:

dynamic i = 12;
i = "text";

Możesz więc zmienić typ według potrzeb. Użyj go w ostateczności; jest to korzystne, ale słyszałem, że wiele dzieje się pod scenami pod względem generowanej IL, a to może mieć cenę za wydajność.

Brian Mains
źródło
7
Z wahaniem powiedziałbym, że to „legalne”. Z pewnością się skompiluje, więc jako taki jest „legalnym kodem” w tym sensie, że kompilator go teraz skompiluje, a środowisko wykonawcze uruchomi go. Ale nigdy nie chciałbym widzieć tego konkretnego fragmentu kodu (lub czegoś podobnego do niego) w żadnym z utrzymywanych przeze mnie kodów, w przeciwnym razie byłoby to przestępstwo prawie odpalające.
Lasse V. Karlsen
6
Jasne, ale byłoby to „uzasadnione” za pomocą „obiektu” zamiast „dynamicznego”. Nie pokazałeś tutaj nic ciekawego na temat dynamiki.
Eric Lippert
W przypadku obiektu musisz rzucić go na odpowiedni typ, aby faktycznie wywołać dowolną z jego metod ... stracisz podpis; możesz wywoływać kod dowolną metodą bez błędu kompilacji, a także błędy w czasie wykonywania. Spieszyło mi się pisać, przepraszam, że nie sprecyzowałem. I @Lasse, zgodziłbym się i prawdopodobnie nie użyję dużo dynamiki.
Brian Mains
1
Przypadek użycia w ostateczności nie został wyjaśniony
denfromufa,
1

Najlepszym przypadkiem użycia zmiennych typu „dynamicznego” było dla mnie, gdy ostatnio pisałem warstwę dostępu do danych w ADO.NET ( używając SQLDataReader ), a kod wywoływał już zapisane procedury składowane. Istnieją setki tych starszych procedur przechowywanych, zawierających większość logiki biznesowej. Moja warstwa dostępu do danych musiała zwrócić jakieś ustrukturyzowane dane do warstwy logiki biznesowej, opartej na języku C #, aby wykonać pewne manipulacje ( chociaż nie ma prawie żadnych ). Każda procedura przechowywana zwraca inny zestaw danych ( kolumny tabeli ). Zamiast tworzyć dziesiątki klas lub struktur do przechowywania zwróconych danych i przekazywania ich do BLL, napisałem poniższy kod, który wygląda dość elegancko i schludnie.

public static dynamic GetSomeData(ParameterDTO dto)
        {
            dynamic result = null;
            string SPName = "a_legacy_stored_procedure";
            using (SqlConnection connection = new SqlConnection(DataConnection.ConnectionString))
            {
                SqlCommand command = new SqlCommand(SPName, connection);
                command.CommandType = System.Data.CommandType.StoredProcedure;                
                command.Parameters.Add(new SqlParameter("@empid", dto.EmpID));
                command.Parameters.Add(new SqlParameter("@deptid", dto.DeptID));
                connection.Open();
                using (SqlDataReader reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        dynamic row = new ExpandoObject();
                        row.EmpName = reader["EmpFullName"].ToString();
                        row.DeptName = reader["DeptName"].ToString();
                        row.AnotherColumn = reader["AnotherColumn"].ToString();                        
                        result = row;
                    }
                }
            }
            return result;
        }
użytkownik1451111
źródło
0
  1. Możesz wywoływać w dynamicznych językach, takich jak CPython, używając pythonnet:

dynamic np = Py.Import("numpy")

  1. Możesz zastosować rzutowanie na ogólne, dynamicgdy zastosujesz na nich operatory numeryczne. Zapewnia to bezpieczeństwo typu i pozwala uniknąć ograniczeń leków generycznych. To w istocie * pisanie kaczek:

T y = x * (dynamic)x, gdzie typeof(x) is T

denfromufa
źródło
0

Innym przykładem użycia do dynamicpisania są metody wirtualne, w których występuje problem z kowariancją lub kontrawariancją. Jednym z takich przykładów jest niesławna Clonemetoda, która zwraca obiekt tego samego typu, co obiekt, do którego został wywołany. Ten problem nie został w pełni rozwiązany za pomocą dynamicznego powrotu, ponieważ omija sprawdzanie typu statycznego, ale przynajmniej nie trzeba przez cały czas używać brzydkich rzutów, jak w przypadku zwykłego object. Innymi słowy, obsady stają się niejawne.

public class A
{
    // attributes and constructor here
    public virtual dynamic Clone()
    {
        var clone = new A();
        // Do more cloning stuff here
        return clone;
    }
}

public class B : A
{
    // more attributes and constructor here
    public override dynamic Clone()
    {
        var clone = new B();    
        // Do more cloning stuff here
        return clone;
    }
}    

public class Program
{
    public static void Main()
    {
        A a = new A().Clone();  // No cast needed here
        B b = new B().Clone();  // and here
        // do more stuff with a and b
    }
}
Frederic
źródło