ParseableSerializer.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #if !NO_RUNTIME
  2. using System;
  3. using System.Net;
  4. using ProtoBuf.Meta;
  5. using System.Reflection;
  6. namespace ProtoBuf.Serializers
  7. {
  8. sealed class ParseableSerializer : IProtoSerializer
  9. {
  10. private readonly MethodInfo parse;
  11. public static ParseableSerializer TryCreate(Type type, TypeModel model)
  12. {
  13. if (type == null) throw new ArgumentNullException("type");
  14. #if PORTABLE || COREFX || PROFILE259
  15. MethodInfo method = null;
  16. #if COREFX || PROFILE259
  17. foreach (MethodInfo tmp in type.GetTypeInfo().GetDeclaredMethods("Parse"))
  18. #else
  19. foreach (MethodInfo tmp in type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly))
  20. #endif
  21. {
  22. ParameterInfo[] p;
  23. if (tmp.Name == "Parse" && tmp.IsPublic && tmp.IsStatic && tmp.DeclaringType == type && (p = tmp.GetParameters()) != null && p.Length == 1 && p[0].ParameterType == typeof(string))
  24. {
  25. method = tmp;
  26. break;
  27. }
  28. }
  29. #else
  30. MethodInfo method = type.GetMethod("Parse",
  31. BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly,
  32. null, new Type[] { model.MapType(typeof(string)) }, null);
  33. #endif
  34. if (method != null && method.ReturnType == type)
  35. {
  36. if (Helpers.IsValueType(type))
  37. {
  38. MethodInfo toString = GetCustomToString(type);
  39. if (toString == null || toString.ReturnType != model.MapType(typeof(string))) return null; // need custom ToString, fools
  40. }
  41. return new ParseableSerializer(method);
  42. }
  43. return null;
  44. }
  45. private static MethodInfo GetCustomToString(Type type)
  46. {
  47. #if PORTABLE || COREFX || PROFILE259
  48. MethodInfo method = Helpers.GetInstanceMethod(type, "ToString", Helpers.EmptyTypes);
  49. if (method == null || !method.IsPublic || method.IsStatic || method.DeclaringType != type) return null;
  50. return method;
  51. #else
  52. return type.GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly,
  53. null, Helpers.EmptyTypes, null);
  54. #endif
  55. }
  56. private ParseableSerializer(MethodInfo parse)
  57. {
  58. this.parse = parse;
  59. }
  60. public Type ExpectedType => parse.DeclaringType;
  61. bool IProtoSerializer.RequiresOldValue { get { return false; } }
  62. bool IProtoSerializer.ReturnsValue { get { return true; } }
  63. public object Read(object value, ProtoReader source)
  64. {
  65. Helpers.DebugAssert(value == null); // since replaces
  66. return parse.Invoke(null, new object[] { source.ReadString() });
  67. }
  68. public void Write(object value, ProtoWriter dest)
  69. {
  70. ProtoWriter.WriteString(value.ToString(), dest);
  71. }
  72. #if FEAT_COMPILER
  73. void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  74. {
  75. Type type = ExpectedType;
  76. if (Helpers.IsValueType(type))
  77. { // note that for structs, we've already asserted that a custom ToString
  78. // exists; no need to handle the box/callvirt scenario
  79. // force it to a variable if needed, so we can take the address
  80. using (Compiler.Local loc = ctx.GetLocalWithValue(type, valueFrom))
  81. {
  82. ctx.LoadAddress(loc, type);
  83. ctx.EmitCall(GetCustomToString(type));
  84. }
  85. }
  86. else
  87. {
  88. ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("ToString"));
  89. }
  90. ctx.EmitBasicWrite("WriteString", valueFrom);
  91. }
  92. void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
  93. {
  94. ctx.EmitBasicRead("ReadString", ctx.MapType(typeof(string)));
  95. ctx.EmitCall(parse);
  96. }
  97. #endif
  98. }
  99. }
  100. #endif