Laden...

Forenbeiträge von chrkon Ingesamt 1 Beiträge

22.01.2014 - 09:25 Uhr

Hallo allerseits,
auch wenn dieser Beitrag schon älter ist, möchte ich noch eine Verbesserung beisteuern.

Der Zugriff auf Structs wie System.Windows.Media.Media3D.Point3D klappt mit der oben beschriebenen Dynamic Method Lösung nicht. Ich habe mir dafür folgenden Unit Test geschrieben:


        [Test]
        public void CheckStructSecondParameter()
        {
            var Pos = new System.Windows.Media.Media3D.Point3D(10.0,20.0,30.0);
            var expected = 20.0;
            
            var daSubProp = new DynamicAccessor(Pos.GetType().GetProperty("Y"));
            var result = daSubProp.Get(Pos);

            Assert.AreEqual(expected, result);
        }

Dieser Test schlägt fehl! Eigenartig ist jedoch, dass die Variable 'result' den Wert 10.0 hat. Es wird also scheinbar die falsche Property ausgelesen. In diesem Fall passt es zur Property "X".

Wenn man nun statt der Point3D Struktur eine Point3D Klasse anlegt


public class myPoint3D
    {
        public myPoint3D(double x, double y, double z)
        {
            X = x; Y = y; Z = z;
        }
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }
    }

und damit den gleichen Test durchführt


        [Test]
        public void CheckClassSecondParameter()
        {
            var Pos = new myPoint3D(10.0, 20.0, 30.0);
            var expected = 20.0;

            var daSubProp = new DynamicAccessor(Pos.GetType().GetProperty("Y"));
            var result = daSubProp.Get(Pos);

            Assert.AreEqual(expected, result);
        }

funktioniert es. Diesmal ist der Test grün.

Bei Stackoverflow habe ich einen Hinweis gefunden: DynamicMethod returns incorrect value when property type is Int64

Ich habe daher die 'EmitGetter' Funktion folgendermaßen angepasst:


        Getter EmitGetter(PropertyInfo propertyInfo)
        {
            DynamicMethod dynMethod = new DynamicMethod("Get",
                typeof(object),
                new Type[] { typeof(object) },
                propertyInfo.DeclaringType);

            ILGenerator il = dynMethod.GetILGenerator();
            MethodInfo getterMethod = propertyInfo.GetGetMethod();

            il.DeclareLocal(typeof(object));
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);

            //// Dieser Codeblock wurde ersetzt. CK,22.1.2014
            //
            // il.EmitCall(OpCodes.Callvirt, getterMethod, null);
            //
            // OpCode box = propertyInfo.PropertyType.IsValueType ?
            //    OpCodes.Box :
            //    OpCodes.Castclass;
            //
            // il.Emit(box, propertyInfo.PropertyType);

            if (propertyInfo.PropertyType.IsValueType)
            {
                if (propertyInfo.ReflectedType.IsValueType)
                {
                    il.Emit(OpCodes.Unbox, propertyInfo.ReflectedType);
                }
                il.EmitCall(OpCodes.Callvirt, getterMethod, null);
                il.Emit(OpCodes.Box, propertyInfo.PropertyType);
            }
            else
            {
                il.EmitCall(OpCodes.Callvirt, getterMethod, null);
                il.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
            }                     
            
            il.Emit(OpCodes.Ret);

            return (Getter)dynMethod.CreateDelegate(typeof(Getter));
        }

Mit dieser Änderung werden die beiden Tests erfolgreich abgeschlossen.

Da ich in meiner Software keinen schreibenden Zugriff benötige, habe ich mir die 'EmitSetter' Funktion nicht näher angesehen. Ich vermute aber, dass dort auch noch Anpassungen notwendig sind.

Viele Grüße,
Christof