"Christmas - the time to fix the computers of your loved ones" « Lord Wyrm

casting probleme in c#

Oculus 19.08.2003 - 21:02 752 7
Posts

Oculus

void
Avatar
Registered: Jun 2001
Location: schlafzimmer
Posts: 856
bin gspannt, obma wer helfen kann

also:
ich hab a klasse "BasicModule", die von Windows.Forms.Control abgeleitet ist und das Interface "IModule" implementiert
wenn ich jetzt zur laufzeit dynamisch aus der dll (assembly) ein objekt dieser klasse instanzieren will, kann ichs zwar auf Control, aber net auf IModule casten

damits euch was vorstellen könnts:

Code:
public class BasicModule : Control, IModule
{ ... }

public interface IModule
{ ... } 

...
ModuleEntity selected = (ModuleEntity)trvQueries.SelectedNode.Tag;
object instance = selected.Library.CreateInstance("DAP." + selected.Description.Class,true);
pnlModule.Controls.Add((Control)instance);
((IModule)instance).DBConnection = dbConnection; 
...

die klasse ModuleEntity enthält informationen über das module und unteranderem eine referenz auf die assembly (in der property "Library")
pnlModule ist ein panel, in dem das control angezeigt werden soll
DBConnection ist eine simple property, die vom interface vorgeschrieben wird

wenn ich das ganze debugge, bekomm ich bei der letzten zeile eine IllegalCastException

weiters habe ich schon ausprobiert, eine abstrakte basis-klasse zu coden, die ihrerseits von Control abgeleitet ist und IModule implementiert
BasicModule ist dann von dieser basis-klasse abgeleitet

fazit: kann wieder auf control casten, aber net auf die basis-klasse !
natürlich auch net auf IModule

und gleich vorweg: ich kann/will/darf das objekt instance nicht händisch auf BasicModule casten, da beliebige modul-klassen daherkommen können, deswegen ja das interface

ich zuck bald aus...
Bearbeitet von Oculus am 25.08.2003, 22:24

Oculus

void
Avatar
Registered: Jun 2001
Location: schlafzimmer
Posts: 856
ich habe ein kleines testprogramm gecoded, um das problem zu veranschaulichen:

Code:
[b]CastTest.cs[/b]

namespace CastTest
{
    public class BaseClass 
    {
        private int x;
        public BaseClass()
        {
            x = 0;
        }
        public int X
        {
            set { x = value; }
            get { return x; }
        }
    }
    public interface ITest
    {
         int Square();
    }
    
    public class TestClass : BaseClass, ITest
    {
        public TestClass()
        {
            X = 5;
        }
        public int Square()
        {
           return X*X;
        }
    }
}

(1) testen wir das ganze mit einer normalen objektinstanz

Code:
[b]Test.cs[/b]

namespace CastTest 
{
    public class MainClass 
    {
        public static void Main(string[] args) 
        {
            try 
            {
                object instance = new CastTest.TestClass();
                int x = ((CastTest.TestClass)instance).Square();
                int y = ((CastTest.ITest)instance).Square();
                System.Console.Write("x ok: " + x);
                System.Console.Write("\ny ok: " + y);
            }
            catch (System.Exception e) 
            {
                System.Console.Write(e.Message);
            }
        }
    }
}

wer ka IDE verwenden will: "csc Test.cs CastTest.cs"
das ganze compiliert und läuft wunderbar


(2) jetzt das ganze mit dymischer instanzierung der klasse aus der assembly heraus

Code:
[b]Test2.cs[/b]

namespace CastTest
{
    public class MainClass 
    {
        public static void Main(string[] args) 
        {
            try 
            {
                System.Reflection.Assembly as = System.Reflection.Assembly.LoadFrom("CastTest.dll");
                System.Console.Write("Assembly loaded: " + as.ToString());
                object instance = as.CreateInstance("CastTest.TestClass");
                System.Console.Write("Instance created: " + instance.ToString());
                int x = ((CastTest.ITest)instance).Square();
                int y = ((CastTest.TestClass)instance).Square();
                System.Console.Write(x);
            }
            catch (System.Exception e) 
            {
                System.Console.Write("\n" + e.Message);
            }
        }
    }
}

zuerst müssen wir CastTest.cs zu einer dll compilieren:
csc /target:library /out:CastTest.dll CastTest.cs

dann Test2.cs. den verweis auf CastTest.cs brauchen wir nur für das interface (und zu testzwecken auch für die klasse selbst. ist schlecht gmacht, weil alles im selben file)
csc Test2.cs CastTest.cs


und siehe da, die assembly wird geladen, die klasse instanziert, aber beim cast gibts eine exception
jetzt schauts euch mal die ausgaben an:
assembly ist CastTest, und das objekt ist vom typ CastTest.TestClass !
also sollte es problemlos zu casten sein
fehlanzeige

tauschts testweise mal die anweisungen mit x= und y=
damit ihr sehts, dass auch der cast auf TestClass fehlschlägt



also... WTF ?
was soll des?
es dürfte ja kein problem mit irgendwelchen assembly-informationen geben, da das objekt ja problemlos instanziert wird, ja sogar vom richtigen typ ist


ps: um das ganze zu übergehn könnt ich zwar über die methoden GetType(), GetMethod() bzw GetProperty() etc. gehen, um meine interface-methoden zur laufzeit zu "suchen" und dann zu invoken, was allerdings sehr unschön wäre

Ringding

Pilot
Avatar
Registered: Jan 2002
Location: Perchtoldsdorf/W..
Posts: 4300
Jetzt wollt ich mich schon beschweren, dass das so ein unübersichtlicher Post ist, dass ihn eh keiner lesen wird, aber das ist gar nicht der Fall.

Das ist jedenfalls ein interessantes Problem, das du hier hast. Würd mich interessieren, woher das kommt. Leider bin ich mit .NET nicht sehr bewandert, hab nur ein paarmal reingeschnuppert.

Ich hätt mir gedacht, dass er vielleicht das Interface zur Laufzeit gar nicht kennt, aber durch die Reference sollte er das doch wohl.

Oculus

void
Avatar
Registered: Jun 2001
Location: schlafzimmer
Posts: 856
nagut, ich hab das problem jetzt umgangen, indem ich auf die Reflection-methoden umgesattelt hab
ist zwar net wirklich schön, aber es funktioniert wenigstens

FYI: über die Reflection klassen kann man sich zur laufzeit jegliche informationen über ein objekt, klasse, assembly etc holen
sowohl name, methoden, interfaces, berechtigungen und und und
und diese natürlich dann manipulieren


Code:
namespace CastTest
{
	public class MainClass
	{
	
		public static void Main(string[] args)
		{
			try
			{
				System.Reflection.Assembly ass = System.Reflection.Assembly.LoadFrom("CastTest.dll");
				System.Console.Write("Assembly loaded: " + ass.ToString());
				object instance = ass.CreateInstance("CastTest.TestClass");
				System.Console.Write("Instance created: " + instance.ToString());
				
				int v = 100;
				instance.GetType().GetProperty("X").SetValue(instance,(object)v,null);
				int x = (int)instance.GetType().GetMethod("Square").Invoke(instance,null);
				//int x = ((CastTest.ITest)instance).Square();
				System.Console.Write(x);
			}
			catch (System.Exception e)
			{
				System.Console.Write("\n" + e.Message);
			}
		}
	}
}

jetzt gehts also
die ursache fürs gundproblem bleibt mir trotzdem a rätsel

Oculus

void
Avatar
Registered: Jun 2001
Location: schlafzimmer
Posts: 856
nach einigem herumforschen in newsgroups hab ich jetzt die lösung gefunden bzw die ursache für das problem

und zwar:
der cast ist deswegen ungültig, weil die definition für das interface sowohl in der assembly, als auch in laufenden programm reincompiliert ist, d.h. man hat zur laufzeit zwei idente definitionen -> runtime kann sich nicht entscheiden und wirft exception

umgehen kann man also nun das ganze, in dem man das interface in eine eigene assembly kompiliert und alle anderen assemblies/executables dann mit verweis auf diese assembly compiliert


code mit den casts drinnen so verändern, dass jede klasse und das interface jeweils in einer eigenen datei ist, und dann neu compilieren:
csc /target:library /out:ITest.dll ITest.cs
csc /target:library /out:BaseClass.dll BaseClass.cs
csc /target:library /out:TestClass.dll TestClass.cs /reference:ITest.dll /reference:BaseClass.dll
csc Test.cs /reference: TestClass.dll


und siehe da, es funzt !

Ringding

Pilot
Avatar
Registered: Jan 2002
Location: Perchtoldsdorf/W..
Posts: 4300
Thanks for the info. Jetzt noch den Thread auf SOLVED stellen, und es ist perfekt :)

Oculus

void
Avatar
Registered: Jun 2001
Location: schlafzimmer
Posts: 856
bittesehr :D

btw: soll deine sig "no space left on this device" heissen?
babelfish ownage :D

Ringding

Pilot
Avatar
Registered: Jan 2002
Location: Perchtoldsdorf/W..
Posts: 4300
But of course
Kontakt | Unser Forum | Über overclockers.at | Impressum | Datenschutz