vitorsousa pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=6be38caeaec8e7808af9d0ab4b34465c84bb6e76
commit 6be38caeaec8e7808af9d0ab4b34465c84bb6e76 Author: Lauro Moura <lauromo...@expertisesolutions.com.br> Date: Thu Jul 11 15:52:52 2019 -0300 csharp: Add helpers to get/set Values from Objects Summary: The user can construct an `Eina.Value` from a plain C# `object`, using reflection to get the correct type of object and construct the correct underlying C value. Also added the `Unwrap()` method to return a C# object representing the wrapped value. Both operations are useful when using `Eina.Value` to Get/Set values from `PropertyInfo` targets as in ``` var v = new Eina.Value(propInfo.GetValue(sourceObj)); ... propInfo.SetValue(targetObj, v.Unwrap()); ``` Currently, containers are not supported. It will be added in a following commit. Depends on D9270 Reviewers: felipealmeida, vitor.sousa, segfaultxavi Reviewed By: vitor.sousa Subscribers: cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9272 --- src/bindings/mono/eina_mono/eina_value.cs | 170 +++++++++++++++++++++++++++++- src/tests/efl_mono/Value.cs | 71 +++++++++++++ 2 files changed, 240 insertions(+), 1 deletion(-) diff --git a/src/bindings/mono/eina_mono/eina_value.cs b/src/bindings/mono/eina_mono/eina_value.cs index 898a4966bb..37412b679b 100644 --- a/src/bindings/mono/eina_mono/eina_value.cs +++ b/src/bindings/mono/eina_mono/eina_value.cs @@ -916,7 +916,86 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value> private Value() { this.Handle = Alloc(); + + if (this.Handle == IntPtr.Zero) + { + throw new OutOfMemoryException("Failed to allocate memory for Eina.Value"); + } + this.Ownership = Ownership.Managed; + MemoryNative.Memset(this.Handle, 0, eina_value_sizeof()); + } + + /// <summary>Creates a new Value from the given C# value.</summary> + /// <param name="obj">The object to be wrapped.</param> + public Value(object obj) : this() + { + var objType = obj.GetType(); + + if (objType == typeof(sbyte)) + { + Setup(ValueType.SByte); + Set((sbyte)obj); + } + else if (objType == typeof(byte)) + { + Setup(ValueType.Byte); + Set((byte)obj); + } + else if (objType == typeof(short)) + { + Setup(ValueType.Short); + Set((short)obj); + } + else if (objType == typeof(ushort)) + { + Setup(ValueType.UShort); + Set((ushort)obj); + } + else if (objType == typeof(int)) + { + Setup(ValueType.Int32); + Set((int)obj); + } + else if (objType == typeof(uint)) + { + Setup(ValueType.UInt32); + Set((uint)obj); + } + else if (objType == typeof(long)) + { + Setup(ValueType.Int64); + Set((long)obj); + } + else if (objType == typeof(ulong)) + { + Setup(ValueType.UInt64); + Set((ulong)obj); + } + else if (objType == typeof(float)) + { + Setup(ValueType.Float); + Set((float)obj); + } + else if (objType == typeof(double)) + { + Setup(ValueType.Double); + Set((double)obj); + } + else if (objType == typeof(string)) + { + Setup(ValueType.String); + Set(obj as string); + } + else if (typeof(Efl.Object).IsAssignableFrom(objType)) + { + Setup(ValueType.Object); + Set(obj as Efl.Object); + } + else + { + throw new ArgumentException($"Unsupported type for direct construction: {objType}"); + } } public Value(IntPtr handle, Ownership ownership = Ownership.Managed) @@ -1390,6 +1469,95 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value> return b; } + /// <summary>Unwrap the value into its underlying C# value. + /// + /// <para>Useful for methods like <see crev="PropertyInfo.SetValue(object, object)" /> + /// as it will unpack the value to it correct C# type.</para> + /// </summary> + /// <returns>The C# value wrapped by this value.</returns> + public object Unwrap() + { + switch (GetValueType()) + { + case ValueType.SByte: + { + sbyte o; + Get(out o); + return o; + } + case ValueType.Byte: + { + byte o; + Get(out o); + return o; + } + case ValueType.Short: + { + short o; + Get(out o); + return o; + } + case ValueType.UShort: + { + ushort o; + Get(out o); + return o; + } + case ValueType.Int32: + { + int o; + Get(out o); + return o; + } + case ValueType.UInt32: + { + uint o; + Get(out o); + return o; + } + case ValueType.Int64: + case ValueType.Long: + { + long o; + Get(out o); + return o; + } + case ValueType.UInt64: + case ValueType.ULong: + { + ulong o; + Get(out o); + return o; + } + case ValueType.Float: + { + float o; + Get(out o); + return o; + } + case ValueType.Double: + { + double o; + Get(out o); + return o; + } + case ValueType.String: + { + string o; + Get(out o); + return o; + } + case ValueType.Object: + { + Efl.Object o; + Get(out o); + return o; + } + default: + throw new InvalidOperationException($"Unsupported value type to unwrap: {GetValueType()}"); + } + } + // Efl.Object conversions are made explicit to avoid ambiguity between // Set(Efl.Object) and Set(Value) when dealing with classes derived from // Efl.Object. @@ -1828,7 +1996,7 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value> if (!GetValueType().IsString()) { throw (new ArgumentException( - "Trying to set non-string value on a string Eina.Value")); + "Trying to set string value on a non-string Eina.Value")); } // No need to worry about ownership as eina_value_set will copy the passed string. diff --git a/src/tests/efl_mono/Value.cs b/src/tests/efl_mono/Value.cs index 75b8e96ca1..1c83344675 100644 --- a/src/tests/efl_mono/Value.cs +++ b/src/tests/efl_mono/Value.cs @@ -1029,5 +1029,76 @@ public static class TestEinaValue { /* Test.Assert(false, "Implement me."); */ /* } */ } + +public static class TestValueFromObject +{ + + private class Holder + { + public int Number { get; set; } + public double Factor { get; set; } + public string Name { get; set; } + public Efl.Object Obj { get; set; } + } + + public static void TestConversionFromToObject() + { + var source = new Holder { + Number = 1984, + Factor = 3.14, + Name = "Orwell", + Obj = new Dummy.TestObject(), + }; + + { + var prop = source.GetType().GetProperty("Name"); + var v = new Eina.Value(prop.GetValue(source)); + + Test.AssertEquals(v.GetValueType(), Eina.ValueType.String); + Test.AssertEquals((string)v, prop.GetValue(source)); + + Test.Assert(v.Set("New value")); + prop.SetValue(source, v.Unwrap()); + Test.AssertEquals(prop.GetValue(source), "New value"); + } + + { + var prop = source.GetType().GetProperty("Factor"); + var v = new Eina.Value(prop.GetValue(source)); + + Test.AssertEquals(v.GetValueType(), Eina.ValueType.Double); + Test.AssertAlmostEquals((double)v, (double)prop.GetValue(source)); + + Test.Assert(v.Set(2.78)); + prop.SetValue(source, v.Unwrap()); + Test.AssertEquals(prop.GetValue(source), 2.78); + } + + { + var prop = source.GetType().GetProperty("Number"); + var v = new Eina.Value(prop.GetValue(source)); + + Test.AssertEquals(v.GetValueType(), Eina.ValueType.Int32); + Test.AssertEquals((int)v, prop.GetValue(source)); + + Test.Assert(v.Set(2012)); + prop.SetValue(source, v.Unwrap()); + Test.AssertEquals(prop.GetValue(source), 2012); + } + + { + var prop = source.GetType().GetProperty("Obj"); + var v = new Eina.Value(prop.GetValue(source)); + + Test.AssertEquals(v.GetValueType(), Eina.ValueType.Object); + Test.AssertEquals((Efl.Object)v, prop.GetValue(source)); + + var newObj = new Dummy.TestObject(); + Test.Assert(v.Set(newObj)); + prop.SetValue(source, v.Unwrap()); + Test.AssertEquals(prop.GetValue(source), newObj); + } + } +} #pragma warning restore 1591 } --