Czy można przypisać obiekt klasy bazowej do odwołania do klasy pochodnej z jawnym rzutowaniem typu?

88

Czy jest możliwe przypisanie obiektu klasy bazowej do odwołania do klasy pochodnej z jawnym rzutowaniem typu w C # ?.

Wypróbowałem to i powoduje to błąd w czasie wykonywania.

Maddy.Shik
źródło

Odpowiedzi:

98

Nie. Odwołanie do klasy pochodnej musi w rzeczywistości odnosić się do wystąpienia klasy pochodnej (lub wartości null). W przeciwnym razie, jak byś się tego spodziewał?

Na przykład:

object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?

Jeśli chcesz mieć możliwość konwersji wystąpienia typu podstawowego na typ pochodny, proponuję napisać metodę tworzenia odpowiedniego wystąpienia typu pochodnego. Lub spójrz ponownie na swoje drzewo dziedziczenia i spróbuj przeprojektować, aby nie trzeba było tego robić w pierwszej kolejności.

Jon Skeet
źródło
72
@Mike: Kod kompiluje się dobrze. Jednak przewraca się w momencie wykonania :)
Jon Skeet
1
Co dokładnie dzieje się, gdy napiszemy Base b = new Derived (); ? Czy utworzy obiekty zarówno dla klasy bazowej, jak i pochodnej?
Ashif Nataliya
3
@Akie: Nie, tworzy pojedynczy obiekt typu Derived, ale możesz traktować Derivedodniesienie jako Baseodniesienie.
Jon Skeet
Więc czy jest jakaś różnica w obiekcie wynikowym dla tych dwóch instrukcji? Baza b = nowa Baza () i Baza b = nowa Pochodna ()? jaka jest korzyść z używania jednego nad drugim?
Ashif Nataliya
4
@ Akie: Tak, jedna tworzy instancję Base, a druga tworzy instancję Derived. Jeśli wywołasz metodę wirtualną, bktóra została zastąpiona w programie Derived, zobaczysz Derivedzachowanie, jeśli masz wystąpienie Derived. Ale nie jest właściwe wchodzenie w szczegóły w wątku komentarzy Stack Overflow - naprawdę powinieneś przeczytać dobrą książkę lub samouczek C #, ponieważ jest to dość fundamentalna sprawa.
Jon Skeet
46

Nie, nie jest to możliwe, ponieważ przypisanie go do odwołania do klasy pochodnej byłoby jak powiedzenie „Klasa podstawowa jest w pełni zdolnym substytutem klasy pochodnej, może robić wszystko, co może zrobić klasa pochodna”, co nie jest prawdą, ponieważ klasy pochodne w ogólnej ofercie więcej funkcji niż ich klasa bazowa (przynajmniej taka jest idea dziedziczenia).

Możesz napisać konstruktor w klasie pochodnej, pobierając jako parametr obiekt klasy bazowej, kopiując wartości.

Coś takiego:

public class Base {
    public int Data;

    public void DoStuff() {
        // Do stuff with data
    }
}

public class Derived : Base {
    public int OtherData;

    public Derived(Base b) {
        this.Data = b.Data;
        OtherData = 0; // default value
    }

    public void DoOtherStuff() {
        // Do some other stuff
    }
}

W takim przypadku należy skopiować obiekt podstawowy i uzyskać w pełni funkcjonalny obiekt klasy pochodnej z domyślnymi wartościami dla elementów pochodnych. W ten sposób możesz również uniknąć problemu wskazanego przez Jona Skeeta:

Base b = new Base();//base class
Derived d = new Derived();//derived class

b.DoStuff();    // OK
d.DoStuff();    // Also OK
b.DoOtherStuff();    // Won't work!
d.DoOtherStuff();    // OK

d = new Derived(b);  // Copy construct a Derived with values of b
d.DoOtherStuff();    // Now works!
Michael Klement
źródło
23

Miałem ten problem i rozwiązałem go, dodając metodę, która przyjmuje parametr typu i konwertuje bieżący obiekt na ten typ.

public TA As<TA>() where TA : Base
{
    var type = typeof (TA);
    var instance = Activator.CreateInstance(type);

     PropertyInfo[] properties = type.GetProperties();
     foreach (var property in properties)
     {
         property.SetValue(instance, property.GetValue(this, null), null);
     }

     return (TA)instance;
}

Oznacza to, że możesz go użyć w swoim kodzie w następujący sposób:

var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1
Markus Knappen Johansson
źródło
Do pobierania i ustawiania właściwości należy użyć typu bieżącej klasy (klasy bazowej), ponieważ są to wartości, które mają zostać odwzorowane na klasę pochodną.
Bowofola
1
Jeśli masz właściwości, których nie można zapisać w typie pochodnym, prawdopodobnie powinieneś zmienić na: if (property.CanWrite) property.SetValue (instance, property.GetValue (this, null), null);
user3478586
10

Jak wielu innych odpowiedziało, nie.

Używam poniższego kodu w tych niefortunnych sytuacjach, gdy muszę użyć typu podstawowego jako typu pochodnego. Tak, jest to naruszenie zasady substytucji Liskova (LSP) i tak, w większości przypadków wolimy skład, a nie dziedziczenie. Podziękowania dla Markusa Knappena Johanssona, na którym opiera się pierwotna odpowiedź.

Ten kod w klasie bazowej:

    public T As<T>()
    {
        var type = typeof(T);
        var instance = Activator.CreateInstance(type);

        if (type.BaseType != null)
        {
            var properties = type.BaseType.GetProperties();
            foreach (var property in properties)
                if (property.CanWrite)
                    property.SetValue(instance, property.GetValue(this, null), null);
        }

        return (T) instance;
    }

Umożliwia:

    derivedObject = baseObect.As<derivedType>()

Ponieważ wykorzystuje odbicie, jest „drogi”. Użyj odpowiednio.

MEC
źródło
Właśnie to wypróbowałem i doszedłem do wniosku, że można to jeszcze ulepszyć, przeciążając operator jawny (a także operator niejawny) .. ale - Kompilator na to nie pozwala: user-defined conversions to or from a base class are not allowed widzę powody takiego stanu rzeczy , ale jestem rozczarowany, ponieważ byłoby fajnie, gdyby to pozwoliło ..
Henrik
@MEC: Zauważyłem, że usunąłeś część `where T: MyBaseClass` i dodałeś if (type.BaseType != null)oświadczenie odnoszące się do A. Markusa Knappena Johanssona. Dlaczego tak jest? Oznacza to, że pozwoliłoby na Typ w wywołaniach, który nie jest pochodną MyBaseClass (ani niczego innego). Zdaję sobie sprawę, że nadal spowoduje to błąd kompilatora, jeśli zostanie przypisany do myDerivedObject, ale jeśli zostanie użyty tylko jako Expression, będzie się kompilował iw czasie wykonywania po prostu utworzy myDerivedObject bez żadnych danych skopiowanych z „myBaseObject”. Nie wyobrażam sobie zastosowania tego.
Tom
@Tom, spóźniona odpowiedź, ale pomyślałem, że to może być przydatne. Prawdopodobnie najlepszą odpowiedzią na Twoje pytanie byłoby stwierdzenie, że nazwa „As” lepiej brzmiałaby „AsOrDefault”. Zasadniczo możemy wziąć ten wynik i porównać go z wartością domyślną, taką jak robimy, używając Linq's SingleOrDefault lub FirstOrDefault.
MEC
7

Nie, nie jest to możliwe, stąd błąd wykonania.

Możesz jednak przypisać wystąpienie klasy pochodnej do zmiennej typu klasy bazowej.

ybo
źródło
7

Rozwiązanie z JsonConvert (zamiast typecast)

Dzisiaj napotkałem ten sam problem i znalazłem proste i szybkie rozwiązanie problemu za pomocą JsonConvert.

var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
Jesse de gans
źródło
Odpowiedziałem na to ponownie poniżej za pomocą metod rozszerzających. Tak, to jest odpowiedź.
Patrick Knott
5

Jak wszyscy tutaj mówili, nie jest to bezpośrednio możliwe.

Metoda, którą preferuję i jest raczej czysta, polega na użyciu programu do mapowania obiektów, takiego jak AutoMapper .

Wykonuje zadanie automatycznego kopiowania właściwości z jednej instancji do drugiej (niekoniecznie tego samego typu).

Mahmoodvcs
źródło
3

Rozwinięcie odpowiedzi @ ybo - nie jest możliwe, ponieważ instancja klasy bazowej nie jest w rzeczywistości instancją klasy pochodnej. Wie tylko o elementach klasy bazowej i nie wie nic o elementach klasy pochodnej.

Powodem, dla którego można rzutować wystąpienie klasy pochodnej na wystąpienie klasy bazowej, jest to, że klasa pochodna w rzeczywistości jest już wystąpieniem klasy bazowej, ponieważ ma już te elementy członkowskie. Nie można powiedzieć, że jest inaczej.

Andy
źródło
3

Można rzutować zmienną wpisaną jako klasę bazową na typ klasy pochodnej; jednak z konieczności spowoduje to sprawdzenie w czasie wykonywania, aby zobaczyć, czy rzeczywisty obiekt, którego to dotyczy, jest odpowiedniego typu.

Po utworzeniu nie można zmienić typu obiektu (co więcej, może nie być tego samego rozmiaru). Możesz jednak przekonwertować wystąpienie, tworząc nowe wystąpienie drugiego typu - ale musisz ręcznie napisać kod konwersji.

Marc Gravell
źródło
2

Nie, to niemożliwe.

Rozważmy scenariusz, w którym ACBus jest klasą pochodną klasy bazowej Bus. ACBus ma funkcje takie jak TurnOnAC i TurnOffAC, które działają na polu o nazwie ACState. TurnOnAC włącza ACState, a TurnOffAC wyłącza ACState. Jeśli spróbujesz użyć funkcji TurnOnAC i TurnOffAC w Bus, nie ma to sensu.

Rohit Dodle
źródło
2
class Program
{
    static void Main(string[] args)
    {
        a a1 = new b();  
        a1.print();  
    }
}
class a
{
    public a()
    {
        Console.WriteLine("base class object initiated");
    }
    public void print()
    {
        Console.WriteLine("base");
    }
}
class b:a
{
    public b()
    {
        Console.WriteLine("child class object");
    }
    public void print1()
    {
        Console.WriteLine("derived");
    }
}

}

kiedy tworzymy obiekt klasy podrzędnej, obiekt klasy bazowej jest inicjowany automatycznie, więc zmienna referencyjna klasy bazowej może wskazywać obiekt klasy potomnej.

ale nie odwrotnie, ponieważ zmienna referencyjna klasy potomnej nie może wskazywać na obiekt klasy bazowej, ponieważ nie jest tworzony żaden obiekt klasy potomnej.

a także zauważ, że zmienna referencyjna klasy bazowej może wywoływać tylko składową klasy bazowej.

shatrudhan kumar
źródło
2

Właściwie JEST na to sposób. Pomyśl o tym, jak możesz użyć Newtonsoft JSON do deserializacji obiektu z json. Zignoruje (lub przynajmniej może) zignorować brakujące elementy i zapełni wszystkie elementy, o których wie.

Oto jak to zrobiłem. Po moim wyjaśnieniu nastąpi mały przykład kodu.

  1. Utwórz wystąpienie obiektu z klasy bazowej i odpowiednio ją wypełnij.

  2. Używając klasy „jsonconvert” biblioteki Newtonsoft json, zserializuj ten obiekt w łańcuch json.

  3. Teraz utwórz obiekt podklasy przez deserializację za pomocą łańcucha json utworzonego w kroku 2. Spowoduje to utworzenie wystąpienia Twojej podklasy ze wszystkimi właściwościami klasy bazowej.

To działa jak urok! Więc… kiedy jest to przydatne? Niektórzy pytali, kiedy to ma sens, i sugerowali zmianę schematu OP, aby uwzględnić fakt, że nie można tego natywnie zrobić z dziedziczeniem klas (w .Net).

W moim przypadku mam klasę ustawień, która zawiera wszystkie „podstawowe” ustawienia usługi. Konkretne usługi mają więcej opcji, a te pochodzą z innej tabeli DB, więc te klasy dziedziczą klasę bazową. Wszystkie mają inny zestaw opcji. Dlatego podczas pobierania danych dla usługi znacznie łatwiej jest PIERWSZE wypełnić wartości przy użyciu wystąpienia obiektu podstawowego. Jedna metoda, aby to zrobić za pomocą pojedynczego zapytania DB. Zaraz po tym tworzę obiekt podklasy przy użyciu metody opisanej powyżej. Następnie wykonuję drugie zapytanie i wypełniam wszystkie wartości dynamiczne obiektu podklasy.

Końcowym wynikiem jest klasa pochodna z ustawionymi wszystkimi opcjami. Powtórzenie tego dla dodatkowych nowych podklas zajmuje tylko kilka linii kodu. Jest to proste i wykorzystuje bardzo wypróbowany i przetestowany pakiet (Newtonsoft), aby magia działała.

Ten przykładowy kod to vb.Net, ale można go łatwo przekonwertować na język C #.

' First, create the base settings object.
    Dim basePMSettngs As gtmaPayMethodSettings = gtmaPayments.getBasePayMethodSetting(payTypeId, account_id)
    Dim basePMSettingsJson As String = JsonConvert.SerializeObject(basePMSettngs, Formatting.Indented)

    ' Create a pmSettings object of this specific type of payment and inherit from the base class object
    Dim pmSettings As gtmaPayMethodAimACHSettings = JsonConvert.DeserializeObject(Of gtmaPayMethodAimACHSettings)(basePMSettingsJson)
Mark Sauer
źródło
przy użyciu C # i Newtonsoft.Json: var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));. Używałbym tego tylko do testów jednostkowych i innych nieprodukcyjnych "hacków"!
thinkOfaNumber
2

Możesz użyć rozszerzenia:

public static void CopyOnlyEqualProperties<T>(this T objDest, object objSource) where T : class
    {
        foreach (PropertyInfo propInfo in typeof(T).GetProperties())
            if (objSource.GetType().GetProperties().Any(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()))
                propInfo.SetValue(objDest, objSource.GetType().GetProperties().First(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()).GetValue(objSource));
    }

W kodzie:

public class BaseClass
{
  public string test{ get; set;}
}
public Derived : BaseClass
{
//Some properies
}

public void CopyProps()
{
   BaseClass baseCl =new BaseClass();
   baseCl.test="Hello";
   Derived drv=new Derived();
   drv.CopyOnlyEqualProperties(baseCl);
   //Should return Hello to the console now in derived class.
   Console.WriteLine(drv.test);

}
Ricardo Figueiredo
źródło
1

Może nie mieć znaczenia, ale mogłem uruchomić kod na obiekcie pochodnym, biorąc pod uwagę jego podstawę. Jest zdecydowanie bardziej hakerski niż bym chciał, ale działa:

public static T Cast<T>(object obj)
{
    return (T)obj;
}

...

//Invoke parent object's json function
MethodInfo castMethod = this.GetType().GetMethod("Cast").MakeGenericMethod(baseObj.GetType());
object castedObject = castMethod.Invoke(null, new object[] { baseObj });
MethodInfo jsonMethod = baseObj.GetType ().GetMethod ("ToJSON");
return (string)jsonMethod.Invoke (castedObject,null);
tstone2077
źródło
1

Możesz to zrobić za pomocą generic.

public class BaseClass
{
    public int A { get; set; }
    public int B { get; set; }
    private T ConvertTo<T>() where T : BaseClass, new()
    {
         return new T
         {
             A = A,
             B = B
         }
    }

    public DerivedClass1 ConvertToDerivedClass1()
    {
         return ConvertTo<DerivedClass1>();
    }

    public DerivedClass2 ConvertToDerivedClass2()
    {
         return ConvertTo<DerivedClass2>();
    }
}

public class DerivedClass1 : BaseClass
{
    public int C { get; set; }
}

public class DerivedClass2 : BaseClass
{
    public int D { get; set; }
}

Dzięki temu podejściu zyskujesz trzy korzyści.

  1. Nie kopiujesz kodu
  2. Nie używasz odbicia (co jest powolne)
  3. Wszystkie Twoje konwersje są w jednym miejscu
adeel41
źródło
1

Wiem, że to jest stare, ale używam go z powodzeniem od dłuższego czasu.

   private void PopulateDerivedFromBase<TB,TD>(TB baseclass,TD derivedclass)
    {
        //get our baseclass properties
        var bprops = baseclass.GetType().GetProperties();
        foreach (var bprop in bprops)
        {
            //get the corresponding property in the derived class
            var dprop = derivedclass.GetType().GetProperty(bprop.Name);
            //if the derived property exists and it's writable, set the value
            if (dprop != null && dprop.CanWrite)
                dprop.SetValue(derivedclass,bprop.GetValue(baseclass, null),null);
        }
    } 
Chris
źródło
1

Połączyłem niektóre fragmenty poprzednich odpowiedzi (dzięki tym autorom) i złożyłem prostą statyczną klasę z dwiema metodami, których używamy.

Tak, to proste, nie, nie obejmuje wszystkich scenariuszy, tak, można go rozszerzyć i ulepszyć, nie, nie jest idealne, tak, można by było bardziej wydajnie, nie to nie jest najlepsza rzecz od czasu krojonego chleba, tak jest w pełni funkcjonalne, solidne mapery obiektów pakietu nuget, które są o wiele lepsze do intensywnego użytku itp., yada yada - ale działa jednak dla naszych podstawowych potrzeb :)

I oczywiście spróbuje odwzorować wartości z dowolnego obiektu na dowolny obiekt, pochodny lub nie (oczywiście tylko właściwości publiczne, które mają takie same nazwy - ignoruje resztę).

STOSOWANIE:

SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };

// creates new object of type "RealPerson" and assigns any matching property 
// values from the puppet object 
// (this method requires that "RealPerson" have a parameterless constructor )
RealPerson person = ObjectMapper.MapToNewObject<RealPerson>(puppet);

// OR

// create the person object on our own 
// (so RealPerson can have any constructor type that it wants)
SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };
RealPerson person = new RealPerson("tall") {Name = "Steve"};

// maps and overwrites any matching property values from 
// the puppet object to the person object so now our person's age will get set to 5 and
// the name "Steve" will get overwritten with "Elmo" in this example
ObjectMapper.MapToExistingObject(puppet, person);

STATYCZNA KLASA UŻYTKOWA:

public static class ObjectMapper
{
    // the target object is created on the fly and the target type 
    // must have a parameterless constructor (either compiler-generated or explicit) 
    public static Ttarget MapToNewObject<Ttarget>(object sourceobject) where Ttarget : new()
    {
        // create an instance of the target class
        Ttarget targetobject = (Ttarget)Activator.CreateInstance(typeof(Ttarget));

        // map the source properties to the target object
        MapToExistingObject(sourceobject, targetobject);

        return targetobject;
    }

    // the target object is created beforehand and passed in
    public static void MapToExistingObject(object sourceobject, object targetobject)
    {
        // get the list of properties available in source class
        var sourceproperties = sourceobject.GetType().GetProperties().ToList();

        // loop through source object properties
        sourceproperties.ForEach(sourceproperty => {

            var targetProp = targetobject.GetType().GetProperty(sourceproperty.Name);

            // check whether that property is present in target class and is writeable
            if (targetProp != null && targetProp.CanWrite)
            {
                // if present get the value and map it
                var value = sourceobject.GetType().GetProperty(sourceproperty.Name).GetValue(sourceobject, null);
                targetobject.GetType().GetProperty(sourceproperty.Name).SetValue(targetobject, value, null);
            }
        });
    }
}
AVH
źródło
1

Możesz użyć konstruktora kopiującego, który natychmiast wywołuje konstruktor instancji lub jeśli konstruktor instancji wykonuje więcej niż przypisania, konstruktor kopiujący przypisuje przychodzące wartości do instancji.

class Person
{
    // Copy constructor 
    public Person(Person previousPerson)
    {
        Name = previousPerson.Name;
        Age = previousPerson.Age;
    }

    // Copy constructor calls the instance constructor.
    public Person(Person previousPerson)
        : this(previousPerson.Name, previousPerson.Age)
    {
    }

    // Instance constructor.
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public int Age { get; set; }

    public string Name { get; set; }
}

Odwołano się do dokumentacji Microsoft C # w obszarze Konstruktor dla tego przykładu, mając ten problem w przeszłości.

mtpultz
źródło
0

Innym rozwiązaniem jest dodanie metody rozszerzenia w następujący sposób:

 public static void CopyProperties(this object destinationObject, object sourceObject, bool overwriteAll = true)
        {
            try
            {
                if (sourceObject != null)
                {
                    PropertyInfo[] sourceProps = sourceObject.GetType().GetProperties();
                    List<string> sourcePropNames = sourceProps.Select(p => p.Name).ToList();
                    foreach (PropertyInfo pi in destinationObject.GetType().GetProperties())
                    {
                        if (sourcePropNames.Contains(pi.Name))
                        {
                            PropertyInfo sourceProp = sourceProps.First(srcProp => srcProp.Name == pi.Name);
                            if (sourceProp.PropertyType == pi.PropertyType)
                                if (overwriteAll || pi.GetValue(destinationObject, null) == null)
                                {
                                    pi.SetValue(destinationObject, sourceProp.GetValue(sourceObject, null), null);
                                }
                        }
                    }
                }
            }
            catch (ApplicationException ex)
            {
                throw;
            }
        }

następnie miej konstruktora w każdej klasie pochodnej, która akceptuje klasę bazową:

  public class DerivedClass: BaseClass
    { 
        public DerivedClass(BaseClass baseModel)
        {
            this.CopyProperties(baseModel);
        }
    }

Opcjonalnie nadpisze właściwości miejsca docelowego, jeśli jest już ustawione (nie jest puste) lub nie.

d.popov
źródło
0

Czy jest możliwe przypisanie obiektu klasy bazowej do odwołania do klasy pochodnej z jawnym rzutowaniem typu w C # ?.

Możliwe są nie tylko jawne, ale także niejawne konwersje.

Język C # nie zezwala na takie operatory konwersji, ale nadal można je pisać w czystym C # i działają. Zauważ, że klasa, która definiuje niejawny operator konwersji ( Derived) i klasa, która używa operatora ( Program), muszą być zdefiniowane w oddzielnych zespołach (np. DerivedKlasa znajduje się w a, do library.dllktórego odwołuje się program.exezawierając Programklasę).

//In library.dll:
public class Base { }

public class Derived {
    [System.Runtime.CompilerServices.SpecialName]
    public static Derived op_Implicit(Base a) {
        return new Derived(a); //Write some Base -> Derived conversion code here
    }

    [System.Runtime.CompilerServices.SpecialName]
    public static Derived op_Explicit(Base a) {
        return new Derived(a); //Write some Base -> Derived conversion code here
    }
}

//In program.exe:
class Program {
    static void Main(string[] args) {
        Derived z = new Base(); //Visual Studio can show squiggles here, but it compiles just fine.
    }
}

Gdy odwołujesz się do biblioteki przy użyciu odwołania do projektu w programie Visual Studio, VS pokazuje zawijasy podczas korzystania z niejawnej konwersji, ale kompiluje się dobrze. Jeśli odwołasz się tylko do library.dll, nie ma zawijasów.

Ark-kun
źródło
Co to za czarna magia?!? Ponadto, w jaki sposób „Derived z = new Base ()” pomaga mi w wykonaniu „BaseCls baseObj; DerivedCls derivedObj; derivedObj = (DerivedCls) baseObj” (Q z OP)? Co też robi System.Runtime.CompilerServices.SpecialNameAttribute? Dokumenty dla każdej wersji od najwcześniejszej dostępnej (2.0) do „aktualnej wersji” (4.6? „Ktoś? Ktokolwiek?”) Nie mówią, co robi, ale mówią „Klasa SpecialNameAttribute nie jest obecnie używana w .NET Framework, ale jest zarezerwowany do użytku w przyszłości. ”. Zobacz: [link] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx ).
Tom
> „Co to za czarna magia?!?” Nazywa się to .Net Framework (CLR, IL, BCL). Zestaw funkcji języków IL, C # i VB nie jest taki sam. W języku VB są funkcje, których C # nie obsługuje. Istnieją funkcje w języku IL, których C # nie obsługuje. Istnieją ograniczenia w C #, które są raczej arbitralne i nie istnieją w bazowym języku IL (jak where T : Delegatelub sparametryzowane właściwości, takie jak indeksatory itp. Itp.).
Ark-kun
> "Poza tym, w jaki sposób" Pochodna z = nowa Base () "pomaga mi to zrobić" BaseCls baseObj; DerivedClsivedObj; derivedObj = (DerivedCls) baseObj "(Q OP)?" Po prostu działa. To rozwiązuje pytanie OP. I nie potrzebujesz nawet wyraźnej obsady.
Ark-kun
> what does System.Runtime.CompilerServices.SpecialName Attribute do?- Służy do oznaczania metod tworzonych przez pewne specjalne wygodne konstrukcje języków .Net wysokiego poziomu: metody dostępu do właściwości, akcesory zdarzeń, konstruktory, operatory, indeksatory, itp. O ile metoda IL nie jest oznaczona specialname, nie będzie widoczna jako właściwość / zdarzenie / konstruktor i zostanie rozpoznany jako normalna metoda. Ręczne oznaczanie metod o odpowiednich nazwach tym atrybutem to po prostu ręczne wykonanie części pracy kompilatora.
Ark-kun
VB.Net ma operatora mocy. C # nie. Jak można przeciążyć operatora zasilania w C # do użytku w VB.Net? Wystarczy zdefiniować op_Exponentmetodę i oznaczyć ją specialnameatrybutem.
Ark-kun
0

Co powiesz na:

public static T As<T>(this object obj)
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
    }
Floare Emil
źródło
0

Najlepszym sposobem dodania wszystkich właściwości podstawowych do elementu pochodnego jest użycie odbicia w costructor. Wypróbuj ten kod bez tworzenia metod lub instancji.

    public Derived(Base item) :base()
    {

        Type type = item.GetType();

        System.Reflection.PropertyInfo[] properties = type.GetProperties();
        foreach (var property in properties)
        {
            try
            {
                property.SetValue(this, property.GetValue(item, null), null);
            }
            catch (Exception) { }
        }

    }
Uraitz
źródło
0

Nie zgadzam się, że to niemożliwe. Możesz to zrobić w ten sposób:

public class Auto 
{ 
    public string Make {get; set;}
    public string Model {get; set;}
}

public class Sedan : Auto
{ 
    public int NumberOfDoors {get; set;}
}

public static T ConvertAuto<T>(Sedan sedan) where T : class
{
    object auto = sedan;
    return (T)loc;
}

Stosowanie:

var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);
Buzz Wilder
źródło
var auto =nadal jest typusedan
bendecko
0

Tak rozwiązałem to dla pól. Jeśli chcesz, możesz wykonać tę samą iterację poprzez właściwości. Możesz chcieć sprawdzić nullitp., Ale taki jest pomysł.

 public static DerivedClass ConvertFromBaseToDerived<BaseClass, DerivedClass>(BaseClass baseClass)
            where BaseClass : class, new()
            where DerivedClass : class, BaseClass, new()
        {
            DerivedClass derived = (DerivedClass)Activator.CreateInstance(typeof(DerivedClass));
            derived.GetType().GetFields().ToList().ForEach(field =>
            {
                var base_ = baseClass.GetType().GetField(field.Name).GetValue(baseClass);
                field.SetValue(derived, base_);

            });

            return derived;
        }
Tamás Panyi
źródło
0

Możesz po prostu serializować obiekt podstawowy do formatu JSON, a następnie deserializować go do obiektu pochodnego.

Metan Patel
źródło
0

Nie w tradycyjnym sensie ... Konwertuj na Json, potem na swój obiekt i bum, gotowe! Jesse powyżej opublikował odpowiedź jako pierwszą, ale nie używał tych metod rozszerzających, które znacznie ułatwiają proces. Utwórz kilka metod rozszerzających:

    public static string ConvertToJson<T>(this T obj)
    {
        return JsonConvert.SerializeObject(obj);
    }
    public static T ConvertToObject<T>(this string json)
    {
        if (string.IsNullOrEmpty(json))
        {
            return Activator.CreateInstance<T>();
        }
        return JsonConvert.DeserializeObject<T>(json);
    }

Umieść je w swojej skrzynce narzędziowej na zawsze, wtedy zawsze możesz to zrobić:

var derivedClass = baseClass.ConvertToJson().ConvertToObject<derivedClass>();

Ach, moc JSON.

Jest kilka pułapek związanych z tym podejściem: naprawdę tworzymy nowy obiekt, a nie rzucamy, co może, ale nie musi, mieć znaczenie. Pola prywatne nie zostaną przeniesione, konstruktory z parametrami nie zostaną wywołane itp. Możliwe, że nie zostanie przypisany jakiś potomny plik json. Strumienie nie są wewnętrznie obsługiwane przez JsonConvert. Jeśli jednak nasza klasa nie opiera się na prywatnych polach i konstruktorach, jest to bardzo skuteczna metoda przenoszenia danych z klasy do klasy bez mapowania i wywoływania konstruktorów, co jest głównym powodem, dla którego chcemy rzutować w pierwszej kolejności.

Patrick Knott
źródło
To nie robi tego, o co prosił OP. To, co robisz, to konstruowanie nowego obiektu odpowiedniego typu dla zmiennej, używając danych z oryginalnego obiektu niewłaściwego typu. To może działać lub nie, ale tak czy inaczej, z pewnością nie jest to przypisywanie obiektu typu klasy bazowej do zmiennej typu pochodnego.
Lasse V. Karlsen
Odpowiedziałem na pytanie: czy można przypisać obiekt klasy bazowej do odwołania do klasy pochodnej z jawnym rzutowaniem typu? Mówiąc nie. Zapewniam alternatywę, która absolutnie działa i jest mniej zagmatwana niż leki generyczne. Jak zaznaczono wiele razy powyżej, może to powodować problemy z przypisywaniem właściwości klasy pochodnej z klasy bazowej, jednak dokładnie tak by to działało (i działa w API), gdyby było to możliwe. To, że moja odpowiedź może być użyta z „niewłaściwego” typu, nie oznacza, że ​​nie można jej użyć dla „właściwego” typu. @ LasseV.Karlsen, wycofaj swoją negatywną ocenę.
Patrick Knott
W przeciwieństwie do większości odpowiedzi tutaj, że łańcuchowe JsonConverts, pokazuję również, jak obsługiwać null.
Patrick Knott