2

I'm trying to make some method like below.
It just add two given objects and return.

object add(object a, object b);

I already tried it with dynamic keyword. Unfortunately this one does not work on iOS. (the platform does not allow runtime code generations)

dynamic add(dynamic a, dynamic b) => a + b;

So, here's my second try and I realized that it's gonna be hell with this way.

private static HybInstance Add(HybInstance a, HybInstance b)
{
    if (a.Is<Int32>()) return AddInt32(a, b);
    /* and so on... */  
}
private static HybInstance AddInt32(HybInstance a, HybInstance b)
{
    Int32 ia = a.As<Int32>();

    if (b.Is<Int16>()) return HybInstance.Int(ia + b.As<Int32>());
    if (b.Is<Int32>()) return HybInstance.Int(ia + b.As<Int32>());
    if (b.Is<Int64>()) return HybInstance.Int64(ia + b.As<Int64>());
    if (b.Is<float>()) return HybInstance.Float(ia + b.As<float>());

    throw new SemanticViolationException($"");
}

// the method should support custom operators too
private static MethodInfo GetAddMethod(HybInstance left) {
    return left.GetMethods("op_Addition").FirstOrDefault();
}

Is there any smarter way to add two objects?


addition:

Here are some examples what I want to do. Just add any kind of objects or throw exception if not possible.

add(1, 1); // 2
add(1, "b"); // exception

add("a", "b"); // "ab"

// and this one also should be work
add(some_class_with_operator_overloading, 10);
Michał Turczyn
  • 32,028
  • 14
  • 47
  • 69
mharti
  • 180
  • 8

5 Answers5

2

Closest you could get using standard .NET types is probably IConvertible:

 static IConvertible Add (IConvertible a, IConvertible b)
 {
     if (a is string) return a.ToString() + b;
     if (b is string) return a + b.ToString();
     // other special cases here
     return a.ToDouble(CultureInfo.CurrentCulture) + b.ToDouble(CultureInfo.CurrentCulture);
 }

 static void Main(string[] args)
 {
     IConvertible a = 1;
     IConvertible b = 2;
     IConvertible s = "string";

     Console.WriteLine(Add(a, b));
     Console.WriteLine(Add(s, s));
     Console.WriteLine(Add(a, s));
 }

Produces

3
stringstring
1string
Ian Mercer
  • 38,490
  • 8
  • 97
  • 133
0

It's impossible to add two objects, because there's nothing about objects that can be added.

It's like you would like add "something" to "something" and expected someone to answer your question with precise answer - it's impossible.

object don't have any fields or properties, so how you'd like to add them??

Unless you have in mind some kind of general rule of adding objects based on their real type, then it would become possible: you would have to check the type of input parameters and then in (rather) huge switch statement return appropriate result (eg. concatenation for strings, simple addition for integers...).

Michał Turczyn
  • 32,028
  • 14
  • 47
  • 69
  • Actually, It's not impossible, my first version of code (with `dynamic`) exactly does what I want. – mharti Apr 10 '19 at 05:49
0

Refelction can be used to walk the properties of both objects, check for name equivalency and numeric data type, then amend property values in a totally generic way:

public static void AddObjects(object oFrom, object oTo)
            {
                if (oFrom != null && oTo != null)
                {
                    foreach (System.Reflection.PropertyInfo f in oFrom.GetType().GetProperties())
                    {
                        if ((oTo).GetType().GetProperty(f.Name) != null)
                        {
                            try
                            {
                                string sType = f.GetType().ToString().ToLower();
                                if (sType==("int") )
                                {                              
                                    oFrom.GetType().GetProperty(f.Name).SetValue(oFrom, (int)(f.GetValue(oFrom)) + (int)(f.GetValue(oTo)));
                                }
                                if (sType=="int32" )
                                {
                                    oFrom.GetType().GetProperty(f.Name).SetValue(oFrom, (Int32)(f.GetValue(oFrom)) + (Int32)(f.GetValue(oTo)));
                                }
                                if (sType==("int64") )
                                {
                                    oFrom.GetType().GetProperty(f.Name).SetValue(oFrom, (Int64)(f.GetValue(oFrom)) + (Int64)(f.GetValue(oTo)));
                                }

                                // keep adding for all numeirc types.  maybe theres a better way?
                            }
                            catch (Exception ex)
                            { }
                        }
                    }
                }
            }
Programnik
  • 1,449
  • 1
  • 9
  • 13
  • `int` and `int32` are the same thing. Not that I would go with this approach, but why all of the string comparisons? `Type type = f.GetType(); if(type == typeof(int)) { ... }` or perhaps even better (if c# 7+) use a pattern matching `switch` – pinkfloydx33 Apr 10 '19 at 09:27
  • Yes, good comment. I am sure there's a more efficient way to do it – Programnik Apr 11 '19 at 00:01
0

Update

Seems like this will not work either

Limitations of Xamarin.iOS

No Dynamic Code Generation

Since the iOS kernel prevents an application from generating code dynamically, Xamarin.iOS does not support any form of dynamic code generation. These include:

  • The System.Reflection.Emit is not available.
  • No support for System.Runtime.Remoting.
  • No support for creating types dynamically (no Type.GetType ("MyType`1")), although looking up existing types (Type.GetType ("System.String") for example, works just fine). Reverse callbacks must be registered with the runtime at compile ti

However

Why does LambdaExpression.Compile() work on iOS (Xamarin)?

On platforms that support code generation, Reflection.Emit-based LambdaCompiler is used.

If that's not available, the expression is interpreted using the interpreter For example, there are classes that interpret Constant and Add.

Original

I am not sure how much mileage you could get out of this, but you could use expressions

public static object Add<T,T2>(T a,T2 b)
{
   var paramA = Expression.Parameter(typeof(T), "a");
   var paramB = Expression.Parameter(typeof(T2), "b");
   var body = Expression.Add(Expression.Convert(paramA, paramB.Type), paramB);
   var add = Expression.Lambda<Func<T, T2, T2>>(body, paramA, paramB).Compile();
   return add(a, b);
}

The assumptions it that it will try to convert to the second parameter type and return of that type.

Obviously you any class will need the appropriate operators

Given

public struct Test
{
   // user-defined conversion from Fraction to double
   public static implicit operator int(Test f)
   {
      return 10;
   }
   public static implicit operator Test(int i)
   {
      return new Test();
   }
   // overload operator *
   public static Test operator +(Test a, Test b)
   {
      return new Test();
   }

}

Example

Console.WriteLine(Add(1, 2));
Console.WriteLine(Add(1, 2.0));
Console.WriteLine(Add(1, new Test()));
TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • Me neither, I just noticed he said that dynamic didn't work, and I didn't even know using dynamic in his code would generate any code, I thought that was just reflection lookup. – Lasse V. Karlsen Apr 10 '19 at 06:08
  • @LasseVågsætherKarlsen seems no No Dynamic Code Generation – TheGeneral Apr 10 '19 at 06:11
  • Thanks, I tried it even though you updated the answer. Just wishing my lucks. but the result was as expected. `ExecutionEngineException: Attempting to call method 'System.Linq.Expressions.Interpreter.LightLambda::MakeRun2' for which no ahead of time (AOT) code was generated.` – mharti Apr 10 '19 at 07:32
  • @user3352855 yeah i figured as much :/ – TheGeneral Apr 10 '19 at 07:36
0

Did you try with generics, 2 things though:

  1. You are wrapping different objects in same wrapper, seems like a design issue, but will leave it since I do not know more.
  2. Most of the int can be directly changed to Int64 and then there will not be that many special cases

I would have a generic function, and would pass it the Add/Combine function which can be defined for different types. Seems to be a cleaner approach.

  public  T Add<T1, T2, T>(T1 firstObject, T2 secondObject, Func<T1,T2,T>Combine)
    {
        var result = Combine(firstObject, secondObject);
        return result;
    }
peeyush singh
  • 1,337
  • 1
  • 12
  • 23
  • That just moving the problem to the `Combine` function – Magnus Apr 10 '19 at 06:10
  • @Magnus think the OP already has a solution, this is just an attempt at a cleaner solution, typically avoiding a huge branch of is else, which to me would be cleaner – peeyush singh Apr 10 '19 at 06:13