pizheng / protobuf-net

Automatically exported from code.google.com/p/protobuf-net
Other
0 stars 0 forks source link

AmbiguousMatchException when there is more than one implicit cast operator with the same signature, but different return type. #208

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Create a type T with two implicit cast operators - one from T to A and 
another from T to B.
2. Specify that A is a surrogate of T in the model.
3. Try to serialize T.

What is the expected output? What do you see instead?
Expected - serialization works.
Actual:
System.Reflection.AmbiguousMatchException occurred
  Message=Ambiguous match found.
  Source=mscorlib
  StackTrace:
       at System.DefaultBinder.SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
  InnerException: 

What version of the product are you using? On what operating system?
rev 424.

Please provide any additional information below.
Implicit cast is probably the only type of functions in C# which can be 
overloaded by their return type only. Currently 
ProtoBuf.Serializers.SurrogateSerializer.GetConversion overlooks this 
peculiarity.

Original issue reported on code.google.com by mark.kha...@gmail.com on 21 Jul 2011 at 11:44

GoogleCodeExporter commented 9 years ago
May I suggest the following code instead of the current implementation of the 
GetConversion method?

        private static bool HasCast(Type type, Type from, Type to, out MethodInfo op)
        {
            const BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
            MemberFilter filter = delegate(MemberInfo m, object criteria)
            {
                if (m.Name != "op_Implicit" && m.Name != "op_Explicit")
                {
                    return false;
                }
                MethodInfo methodInfo = (MethodInfo)m;
                if (methodInfo.ReturnType != to)
                {
                    return false;
                }

                ParameterInfo[] paramTypes = methodInfo.GetParameters();
                return paramTypes.Length == 1 && paramTypes[0].ParameterType == from;
            };
            MemberInfo[] found = type.FindMembers(MemberTypes.Method, flags, filter, null);
            if (found.Length == 0)
            {
                op = null;
                return false;
            }
            op = (MethodInfo)found[0];
            return true;
        }

        public MethodInfo GetConversion(bool toTail)
        {
            Type to = toTail ? declaredType : forType;
            Type from = toTail ? forType : declaredType;
            MethodInfo op;
            if (HasCast(declaredType, from, to, out op) || HasCast(forType, from, to, out op))
            {
                return op;
            }
            throw new InvalidOperationException("No suitable conversion operator found fopr surrogate: " +
                forType.FullName + " / " + declaredType.FullName);
        }

Original comment by mark.kha...@gmail.com on 21 Jul 2011 at 12:07

GoogleCodeExporter commented 9 years ago
yes, that looks better; I've made an intermediate commit including a patch for 
this - similar logic to the above, but slightly different code. Thanks for the 
input.

Original comment by marc.gravell on 21 Jul 2011 at 1:02