Temat: Problem z rzutowaniem obiektów

Witam Państwa,
potrzebuję pomocy w związku z pewnym problemem (C#).

Fragment kodu (problematyka):


public partial class Form1 : Form
{
public Form1()
{
Form frm1 = new Form();
Form2 frm2 = new Form2();
frm2 = (Form2)frm1; // tutaj wyrzuca błąd
}
}

public class Form2 : Form
{
}


Rozumiem, że nie można rzutować na obiekt klasy, która dziedziczy po klasie bazowej. Czy komuś udało się obejść ten problem i czy jest to wogóle wykonalne?Rafał T. edytował(a) ten post dnia 19.12.09 o godzinie 21:15

konto usunięte

Temat: Problem z rzutowaniem obiektów

Ale jaki problem? I co w ogóle chcesz tym kodem osiągnąć? Może miałeś na myśli rzutowanie w drugą stronę? Z Form3 na Form?

konto usunięte

Temat: Problem z rzutowaniem obiektów

Rafał T.:
Witam Państwa,
potrzebuję pomocy w związku z pewnym problemem (C#).

Fragment kodu (problematyka):


public partial class Form1 : Form
{
public Form1()
{
Form frm1 = new Form();
Form2 frm2 = new Form2();
frm2 = (Form2)frm1; // tutaj wyrzuca błąd
}
}

public class Form2 : Form
{
}


Rozumiem, że nie można rzutować na obiekt klasy, która dziedziczy po klasie bazowej. Czy komuś udało się obejść ten problem i czy jest to wogóle wykonalne?[edited]Rafał T.

Takiego rzutowania chyba nie zrobisz w zadnym jezyku obiektowym. Np. Jak mialby zachowac sie kompilator jak bys w Form2 nadpisywal cos z Form ? nie wiedzialby jak to zrzutowac.
Stanisław P.

Stanisław P. Software designer

Temat: Problem z rzutowaniem obiektów

Rafał T.:
Rozumiem, że nie można rzutować na obiekt klasy, która dziedziczy po klasie bazowej. Czy komuś udało się obejść ten problem i czy jest to wogóle wykonalne?

Generalnie rzutować na obiekt nie można ;) Ale tak serio, to lepiej napisz co tak naprawdę chciałeś osiągnąć i dlaczego. Może wymyślimy jakieś obejście.
Prawdopodobnie to co chcesz zrobić da się załatwić interfejsami.

konto usunięte

Temat: Problem z rzutowaniem obiektów

Rafał T.:
Witam Państwa,
potrzebuję pomocy w związku z pewnym problemem (C#).

Fragment kodu (problematyka):


public partial class Form1 : Form
{
public Form1()
{
Form frm1 = new Form();
Form2 frm2 = new Form2();
frm2 = (Form2)frm1; // tutaj wyrzuca błąd
}
}

public class Form2 : Form
{
}


Rozumiem, że nie można rzutować na obiekt klasy, która dziedziczy po klasie bazowej. Czy komuś udało się obejść ten problem i czy jest to wogóle wykonalne?

j.w tak nie można zrobić, bo kompilator nie wie co ma z tym zrobić, jeśli byś się uparł możesz to w C# zrobić przez operator explicit ale sam musisz napisać implementacje tego co się ma tam stać a żeby dobrze ci się to prze konwertowało.

Lecz nie wydaje mi się żeby o to ci chodziło, gdyż twój problem na pewno da się łatwiej rozwiązać, ale musisz powiedzieć o co dokładnie chodzi.

Najprawdopodobniej zadowoli cie wspólny interfejs na tych obiektach i zestaw wspólnych operacji miedzy nimi, ba nawet wspólna klasa bazowa gdzie zrobisz dziedziczenie z Form i Form1 i 2 będą z dziedziczyć z twojej konkretnej klasy, wtedy rzutujesz na konkretną klasę bazową i po zabawie :).

Podsumowując:

Suche fakty są takie że przez explicit to ruszy ale musisz dać własną implementacje konwersji, ale czy tego na pewno chcesz?.Bartosz Adamczewski edytował(a) ten post dnia 20.12.09 o godzinie 00:00

Temat: Problem z rzutowaniem obiektów

Witam ponownie i dziękuję za Wasze zainteresowanie. Oczywiście mieliście rację, takie rzutowanie jest niedozwolone - po kilku dniach snu wszystko wygląda już inaczej.

Niemniej mam problem, z którym nie mogę sobie poradzić.

Fragment kodu:

(...)

Assembly asm = Assembly.LoadFrom(sNazwaPliku);
foreach (Type asmTyp in asm.GetTypes())
{
if (asmTyp.IsPublic && !asmTyp.IsAbstract)
{
Type Interfejs = asmTyp.GetInterface("JakasPrzestrzen.MojInterface", true);
if (Interfejs != null)
{
// to działa
// Form oModul = (Form)Activator.CreateInstance(asmZestaw.GetType(asmTyp.ToString()));
// tworzona jest instancja klasy Form1, która dziedziczy po Form

// takie rzutowanie jest już niepoprawne
MojaForma oModul = (MojaForma)Activator.CreateInstance(asmZestaw.GetType(asmTyp.ToString()));
// MojaForma to klasa, która również dziedziczy po Form
// zawiera przeładowane metody, która zmieniają wygląd formy itp.
}
}
}

(...)

Czy ktoś ma jakiś pomysł, który pozwoliłby mi na realizacje takiego rzutowania, ewentualnie zaproponowałby obejście problemu w inny sposób?Rafał T. edytował(a) ten post dnia 22.12.09 o godzinie 17:19

konto usunięte

Temat: Problem z rzutowaniem obiektów

Rafał T.:
Czy ktoś ma jakiś pomysł, który pozwoliłby mi na realizacje takiego rzutowania, ewentualnie zaproponowałby obejście problemu
Od razu mówię, że od początku wątku nie rozumiem o co dokładnie Tobie chodzi i nie chcesz wprost napisać, ale:
- jeżeli chcesz wybrać z Assembly taki typ, który implementuje twój interfejs MyInterface
to chyba funkcja Type.IsAssignableFrom jest tym, czego szukasz. Oto przykład
foreach(MyInterface asmTyp in
this.GetType().Assembly //tutaj bylo LoadFrom(sNazwaPliku)
.GetTypes()
.Where(t =>
t.IsPublic
&& !t.IsAbstract
&& typeof(MyInterface).IsAssignableFrom(t)
)
.Select(t => (MyInterface)Activator.CreateInstance(t))
) {
Trace.WriteLine(asmTyp.GetType().Name);
//zrob z asmTyp cokolwiek
}

Użyłem trochę Linq aby zwiększyć czytelność kodu. Przykład się skompiluje o ile dorzucisz gdzieś definicję interfejsu public interface MyInterface {}

Edit: usunąłem użycie tagów GL w ramach _code_maciek kański edytował(a) ten post dnia 22.12.09 o godzinie 19:27

konto usunięte

Temat: Problem z rzutowaniem obiektów

Rzutujesz obiekt mniej wyspecjalizowany na bardziej wyspecjalizowany, j.w kompilator nie wie co z tym zrobić bo to rzutowanie stratne, użyj interfejsu jeśli metody ci się pokrywają w obu klasach, ew explicit ale nie nie to zbędne.

konto usunięte

Temat: Problem z rzutowaniem obiektów

Widząc typ bazowy (żeby nie kusić losu, zastanów się nad interfejsem;), przeszukiwanie assembly i Activator.CreateInstance od razu przychodzi mi do głowy jedno narzędzie - MEF. Napisz coś bardziej ogólnego o Twoim celu np. "buduje system pluginów do aplikacji...", a może ktoś zaserwuje Ci gotowy przepis.

Temat: Problem z rzutowaniem obiektów

Panowie dziękuję za pomoc, ale w dalszym ciągu jest problem. Po wymianie maili z Maćkiem stwierdziłem, że opiszę trochę więcej na temat projektu co może naświetli szybciej rozwiązanie.

Otóż... zgadza się, buduje system wtyczek, a właściwie mam już zbudowany. Wszystko działa jak należy. W tym miejscu ważne jest, abym wspomniał, że moje biblioteki nie korzystają w żaden sposób z referencji do plików zawierających interface'y czy klasy pośrednie. Na chwilę obecną udało mi się osiągnąć wszystko czego potrzebowałem: mam dwukierunkową komunikację, tzn. jestem w stanie wywoływać metody z aplikacji Host jak również z biblioteki przez aplikację Host. Formę okna głównego z biblioteki wywołuję w sposób opisany przeze mnie powyżej (a poniżej przypomnę):


Assembly asm = Assembly.LoadFrom("biblioteka.dll");
foreach (Type asmTyp in asm.GetTypes())
{
if (asmTyp.IsPublic && !asmTyp.IsAbstract)
{
Type Interfejs = asmTyp.GetInterface("JakasPrzestrzen.MojInterface", true);
// MojInterface to tylko informacja dla Host, o tym, że biblioteka ma być
// załadowana przez program - interface nie zawiera żadnych deklaracji
if (Interfejs != null)
{
// to działa wspaniale:
// Form oModul = (Form)Activator.CreateInstance(asmZestaw.GetType(asmTyp.ToString()));
// tworzona jest instancja klasy Form1, która dziedziczy po Form

// a to już nie:
MojaForma oModul = (MojaForma)Activator.CreateInstance(asmZestaw.GetType(asmTyp.ToString()));
// MojaForma to klasa, która również dziedziczy po Form
// zawiera przeładowane metody, która zmieniają wygląd formy itp.
}
}
}


Pytanie brzmi dokładnie tak: w jaki sposób podmienić w locie obiekt klasy Form1 (ktory dziedziczy po Form) na obiekt klasy MojaForma (ktory również dziedziczy po Form i zawiera przeładowane metody zmieniające wygląd formy) ewentualnie w jaki sposób dokonać konwersji obiektu bazowego na obiekt docelowy? Przypominam, że w moim projekcie (plugin) nie wykorzystuję referencji do pliku z interface'ami (tak, wiem, że byłoby to znacznie prostsze rozwiązanie, ale niestety nie używam).

Jeszcze prościej:
- MojaForma działa na hoście i ma za zadanie skonfigurować wygląd graficzny dowolnego okienka. Do tego służą metody CreateParams, OnActivated, DisplayRectangle, OnCreateControl itd. itp. Reasumując: MojaForma ma jedną odpowiedzialność - skonfigurować wygląd;
- konkretne okienka tworzone są w pluginie - są to najzwyklejsze formularze tworzone przy użyciu designera VS które nic nie wiedzą o tym, że będą użyte przez hosta (i w jaki sposób);
- w efekcie chce otrzymać efekt połączenia jednego i drugiego, tzn. aby utworzony w pluginie Form1 jakoś magicznie - w runtime - odziedziczył z MojaForma wspomniane metody.

Chętnie udzielę innych informacji, jeśli ktoś zapyta, bo sam już nie wiem co jeszcze mógłbym napisać.

Edit: jestem w stanie pójść na kompromis i pozostawić rzutowanie na Form, ale mam zupełnie inny problem opisany w tym wątku.Rafał T. edytował(a) ten post dnia 23.12.09 o godzinie 23:59

konto usunięte

Temat: Problem z rzutowaniem obiektów

Rafał T.:
Pytanie brzmi dokładnie tak: w jaki sposób podmienić w locie obiekt klasy Form1 (ktory dziedziczy po Form) na obiekt klasy MojaForma (ktory również dziedziczy po Form i zawiera przeładowane metody zmieniające wygląd formy)

Jest sposób ale nie wiem czy chcesz się w to mieszać, możesz podmienić wskaźnik do metod w obiekcie z klasy Form1 na na wskaźniki z MojaForma dzięki temu w runtime wywołają ci się metody z innego obiektu. To jest pierwsze rozwiązanie które jest wykonalne w 100% ale niebezpieczne, pamiętaj że jawnie mieszasz w pamięci i podmieniasz adresy, wiec musisz dostarczyć implementacje na architekturę x86 jak i x64 jako że NET ma inny rozkład pamięci dla obu, jeśli MS zmieni platformę net w nowej wersji to zgadnij co się wtedy stanie, jeśli alokacja typu zmieni nagle adres :).

Kolejny prostszy pomysł który przychodzi mi do głowy to stworzyć nowy Typ (TypeBuilder) w pamięci, który będzie dziedziczyć po Form oraz dostarczyć w nim interfejs klasy MojaForma gdzie będziesz owrappowywać metody z Form1.

W obu przypadkach wymagana znajomość ILa, jeśli chcesz mogę pomóc nie jest to trudne wbrew pozorom.

O tej godz nic więcej do głowy mi nie przychodzi :), jutro jeszcze to przemyśle.

konto usunięte

Temat: Problem z rzutowaniem obiektów

Rafał T.:
tzn. aby utworzony w pluginie Form1 jakoś magicznie - w runtime - odziedziczył z MojaForma wspomniane metody.
no właśnie jak Bartosz pisze bez ILa chyba się nie obejdzie. Ale nie trzeba tego robić ręcznie, ja mogę jako tako rozwiązanie przez Unity.InterceptionExtension podesłać, są jeszcze inne frameworki do tych celów.

Temat: Problem z rzutowaniem obiektów

Co do podmiany konkretnych wskaźników to wg mnie kłóci się to trochę z koncepcją platformy - z założenia mamy przecież otrzymywać produkt, który jest "idealnie" dopasowany do każdego komputera. Pomysł jednak dość ciekawy.

Poprosiłbym za to o naświetlenie sprawy w tym drugim przypadku (TypeBuilder), gdyż brzmi to ciekawie. Rozumiem, że jest to identyczna zasada działania jak w przypadku Unity?

konto usunięte

Temat: Problem z rzutowaniem obiektów

I przykazanie programisty mówi: "Przedkładaj kompozycję nad dziedziczenie." :) Do tego dorzucę zasadę SRP (pojedynczej odpowiedzialności). O ile dobrze Ciebie zrozumiałem:

1. Masz aplikację hosta.
2. W aplikacji konfigurujesz wygląd okien.
3. Masz pluginy, które mają na ekranie wyświetlić okno w stylu ustawionym w głównej aplikacji.

Moja propozycja to:

Wydzielić z MojaForm obiekt odpowiedzialny za konfigurowanie wyglądu formy - np. FormSkin i przekazywać go do pluginów jako parametr. Metody w obiekcie FormSkin powinny przyjmować jako parametr Form, który będzie zmieniany


// skin configuration
var skin = new FormSkin();
...

// load plugin
var plugin = this.LoadPlugin("plugin.dll");

// start pluginu
plugin.Start(skin);



A w samym pluginie:



public void Start(FormSkin skin)
{
this._skin = skin
...
}

protected void DrawForm()
{
this._skin.SomeMethod(this);
}



Rafale zastanów się czy wszystko jest w porządku z Twoim kodem, jeżeli do uzyskania prostej funkcjonalności potrzebujesz "magicznych" trików.

Pozdrawiam,Jarek D. edytował(a) ten post dnia 28.12.09 o godzinie 15:47

konto usunięte

Temat: Problem z rzutowaniem obiektów

Rafał T.:
Co do podmiany konkretnych wskaźników to wg mnie kłóci się to trochę z koncepcją platformy - z założenia mamy przecież otrzymywać produkt, który jest "idealnie" dopasowany do każdego komputera. Pomysł jednak dość ciekawy.

Poprosiłbym za to o naświetlenie sprawy w tym drugim przypadku (TypeBuilder), gdyż brzmi to ciekawie. Rozumiem, że jest to identyczna zasada działania jak w przypadku Unity?

Ok napisałem szybki kawałek kodu dla metody nr 2.
Nie jest on szczególnie dobry ani też jak powinno się to robić aby kod wyglądał ładnie ale nie chciałem zaciemniać ponieważ i tak znajdziesz dużo informacji na ten temat, a to ma tylko pokazać jaka jest idea.

Jeśli chcesz to możesz zobaczyć klasę DynamicTypeBuilder w moim prywatnym projekcie (link jest u mnie w profilu) gdzie kod jest dynamicznie wstrzykiwany, ale sadze że to powinno ci wystarczyć żeby zrobić własną implementację.


public class Form1 : Form
{
public void MethodA(int x)
{
Console.WriteLine("a");
}
}

public class MyForm : Form
{

public void MethodB()
{
Console.WriteLine("b");
}
}

class Program
{
public static void Main(string[] args)
{
AssemblyName assemblyName = new AssemblyName("DynamicTypeAssembly");
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule(assemblyName.Name);

TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicType", TypeAttributes.Public, typeof(Form));

MethodBuilder mb = null;
ILGenerator ilGen = null;
Type form1Type = typeof(Form1);
ParameterInfo[] paramInfo = null;

foreach (var method in form1Type.GetMethods())
{
mb = typeBuilder.DefineMethod(method.Name, method.Attributes);
paramInfo = method.GetParameters();
ilGen = mb.GetILGenerator();

ilGen.DeclareLocal(form1Type);

ilGen.Emit(OpCodes.Newobj, form1Type.GetConstructor(new Type[] {}));
ilGen.Emit(OpCodes.Stloc_0);
ilGen.Emit(OpCodes.Ldloc_0);

foreach(var param in paramInfo)
{
ilGen.Emit(OpCodes.Ldarg_S, param.ParameterType);
}

ilGen.Emit(OpCodes.Callvirt, method);
ilGen.Emit(OpCodes.Ret);
}

foreach (var method in typeof(MyForm).GetMethods())
{
mb = typeBuilder.DefineMethod(method.Name, method.Attributes);
paramInfo = method.GetParameters();
ilGen = mb.GetILGenerator();

foreach (var param in paramInfo)
{
ilGen.Emit(OpCodes.Ldarg_S, param.ParameterType);
}

ilGen.Emit(OpCodes.Callvirt, method);
ilGen.Emit(OpCodes.Ret);
}

Type finalType = typeBuilder.CreateType();

//now you got it but you need invoke the methods dynamicly
//unless you put a interface on the classes.

object form = Activator.CreateInstance(finalType);
}

}

konto usunięte

Temat: Problem z rzutowaniem obiektów

Jeśli chcesz to możesz zobaczyć klasę DynamicTypeBuilder w moim prywatnym projekcie (link jest u mnie w profilu) gdzie
kod jest dynamicznie wstrzykiwany
Nie mogę teraz pobrać twojego projektu ale trochę go zareklamuj: chcesz powiedzieć, że jesteś w stanie przepisać body jednej funkcji do drugiej? Czy też jako dynamicznie wstrzykiwany kod rozumiesz po prostu ręczne generowanie ciała metody przez ilGen.Emit?

Ja próbowałem coś zrobić z MethodBody.GetILAsByteArray ale dość szybko poległem, głównie na dekodowaniu tokenów. W całym projekcie Unity.Interception nie znalazłem nigdzie odwołania do GetILAsByteArray więc nie miałem skąd się nauczyć. Wiem, że Reflector jakoś by to potrafił zrobić.

konto usunięte

Temat: Problem z rzutowaniem obiektów

maciek kański:
Ja próbowałem coś zrobić z MethodBody.GetILAsByteArray ale dość szybko poległem, głównie na dekodowaniu tokenów. W całym projekcie Unity.Interception nie znalazłem nigdzie odwołania do GetILAsByteArray więc nie miałem skąd się nauczyć. Wiem, że Reflector jakoś by to potrafił zrobić.

Ja z tego korzystam ale, nie polecam jest to metoda super niebezpieczna (tzn nie aż tak), zarządzanie tym kodem jest niezwykle trudne w kontekście innej metody, niemniej jednak to działa, obecnie zastosowanie tej metody przydaje mi się do logowania bez podania parametrów

[LOG]
void MethodA() {}

Efekt tego będzie taki że do metody MethodA zostanie wstrzyknięty kod Logger.Write("the method was executed: " + MethodInfo.GetCurrentMethod().Name)

Pewnie znalazło by się jeszcze kilka przypadków w których ta metoda ma sens, bo kod musi się wykonać w kontekście tej oryginalnej metody.

Przy robieniu operacji wstrzykiwania napotkałem wiele problemów, gdzie wydawało mi się że wszystko będzie chodzić ale jak tylko lekko kod został zmodyfikowany to cała praca szła w piach :) Natomiast dekompilacja tą metodą jest znacznie łatwiejsza i to działa dobrze jeśli chcesz tylko pokazać kod jako string.

W każdym innym przypadku lepiej i bezpieczniej jest zrobić nowy typ w pamięci jako wrapper na nasz oryginalny typ i dołożyć wywołanie metod przed lub po naszej metodzie, albo np przekazać jakieś nowe parametry do naszej metody, zwrócić coś innego etc.

Temat: Problem z rzutowaniem obiektów

Jarku Twoje rozwiązanie zakłada, że twórca pluginu wie, że ma się "czegoś" spodziewać oraz musi zadbać o odpowiednie tego wykorzystanie (w najprostszej wersji musi chociaż połączyć odpowiednie zdarzenia formy z metodami z obiektu, który otrzymuje). Nie spełnia to założeń systemu - twórca pluginu nie powinien sobie zawracać głowy w jaki sposób jego formy będą wyświetlane na ekranie (a jeśli w przyszłości projektant systemu stwierdzi, że istnieje potrzeba oprogramowania dodatkowych zdarzeń?).

Co do poprawności kodu to myślę, że jeszcze nie trzeba mnie linczować :). Wolę już, abyś stwierdził, że "utrudniam sobie życie niepotrzebnie" - bo z tym jestem w stanie się zgodzić. Wątek, nad którym rozwodzimy się od kilku dni można by rozwiązać za pomocą odpowiednich referencji oraz interface'ów. Ot, moja fanaberia, aby zrobić to inaczej, gdyby nie czas, który spędziłem, aby program pracował w ten, a nie inny sposób (zgodnie z założeniem) pewnie już dawno zmieniłbym koncepcję i było by po problemie.

Poza tym każdy wyniesie z tego lekcję.

A wszystkim bardzo dziękuję za zaangażowanie i pomoc (szczególnie Maćkowi i Bartoszowi, którzy podali wiele ciekawych rozwiązań).Rafał T. edytował(a) ten post dnia 30.12.09 o godzinie 11:09

konto usunięte

Temat: Problem z rzutowaniem obiektów

Rafał T.:
Jarku Twoje rozwiązanie zakłada, że twórca pluginu wie, że ma się "czegoś" spodziewać oraz musi zadbać o odpowiednie tego wykorzystanie (w najprostszej wersji musi chociaż połączyć odpowiednie zdarzenia formy z metodami z obiektu, który otrzymuje). Nie spełnia to założeń systemu - twórca pluginu nie powinien sobie zawracać głowy w jaki sposób jego formy będą wyświetlane na ekranie (a jeśli w przyszłości projektant systemu stwierdzi, że istnieje potrzeba oprogramowania dodatkowych zdarzeń?).

Teoretycznie mógłbyś zrobić klasę bazową, w której miałbyś zaimplementowaną "obsługę skórek" i wymagał od twórców pluginów aby po niej dziedziczyli. Tak teoretycznie ;)
Co do poprawności kodu to myślę, że jeszcze nie trzeba mnie linczować :).

Nigdy w życiu! Przepraszam, jeżeli poczułeś się w jakiś sposób urażony. Po prostu zadałem Tobie pytanie, które zadaje sam sobie jeżeli mój kod zaczyna przypominać zalążek Skynet'u i jest prawdopodobne, że w przeciągu pół roku osiągnie świadomość i zniszczy gatunek ludzki ;)

Pozdrawiam,Jarek D. edytował(a) ten post dnia 30.12.09 o godzinie 19:01



Wyślij zaproszenie do