public override T Read(TProtocol protocol)
        {
            TProtocolReader reader = new TProtocolReader(protocol);

            reader.ReadStructBegin();

            IDictionary <short, Object> data = new Dictionary <short, Object>(_metadata.Fields.Count());

            while (reader.NextField())
            {
                short fieldId = reader.GetFieldId();

                // do we have a codec for this field
                IThriftCodec codec;
                if (!_fields.TryGetValue(fieldId, out codec))
                {
                    reader.SkipFieldData();
                    continue;
                }

                // is this field readable
                ThriftFieldMetadata field = _metadata.GetField(fieldId);
                if (field.ReadOnly || field.Type != FieldKind.ThriftField)
                {
                    reader.SkipFieldData();
                    continue;
                }

                // read the value
                Object value = reader.ReadField(codec);
                if (value == null)
                {
                    if (field.Required == ThriftFieldAttribute.Requiredness.Required)
                    {
                        throw new TProtocolException($"'{field.Name}(id: {fieldId})' is a required field, but it was not set, thrift type: '{field.ThriftType.ToString()}', codec: '{codec.GetType().Name}'");
                    }
                    else
                    {
                        continue;
                    }
                }
                data[fieldId] = value;
            }
            reader.ReadStructEnd();

            // build the struct
            return(ConstructStruct(data));
        }
        protected Object GetFieldValue(Object instance, ThriftFieldMetadata field)
        {
            IThriftExtraction extraction;

            if (field.TryGetExtraction(out extraction))
            {
                if (extraction is ThriftFieldExtractor)
                {
                    ThriftFieldExtractor thriftFieldExtractor = (ThriftFieldExtractor)extraction;
                    return(thriftFieldExtractor.Field.GetValue(instance));
                }
                else if (extraction is ThriftMethodExtractor)
                {
                    ThriftMethodExtractor thriftMethodExtractor = (ThriftMethodExtractor)extraction;
                    return(thriftMethodExtractor.Method.Invoke(instance, null));
                }
                throw new ThriftyException($"Unsupported field extractor type {extraction.GetType().FullName}.");
            }
            throw new ThriftyException($"No extraction present for {field}.");
        }
        internal ThriftMethodMetadata(String serviceName, MethodInfo method, ThriftCatalog catalog)
        {
            Guard.ArgumentNullOrWhiteSpaceString(serviceName, nameof(serviceName));
            Guard.ArgumentNotNull(method, nameof(method));
            Guard.ArgumentNotNull(catalog, nameof(catalog));

            this.Order = (ThriftCatalog.GetMethodOrder(method) ?? int.MaxValue);

            ThriftMethodAttribute thriftMethod = method.GetCustomAttribute <ThriftMethodAttribute>();

            if (thriftMethod == null)
            {
                throw new ArgumentException($"Method '{method.DeclaringType.Name}.{method.Name}' is not annotated with {nameof(ThriftMethodAttribute)}.", nameof(method));
            }
            if (method.IsStatic)
            {
                throw new ArgumentException($"Method '{method.DeclaringType.Name}.{method.Name} is a static method.", nameof(method));
            }

            this.Name = String.IsNullOrWhiteSpace(thriftMethod.Name) ? method.Name : thriftMethod.Name.Trim();

            this.QualifiedName = GetQualifiedName(serviceName, Name);

            //this.QualifiedName = $"{serviceName}.{this.Name}";
            this.ReturnType = catalog.GetThriftType(method.ReturnType);

            var builder    = ImmutableList.CreateBuilder <ThriftFieldMetadata>();
            var parameters = method.GetParameters();
            int index      = 0;

            foreach (var p in parameters)
            {
                ThriftFieldMetadata fieldMetadata = CreateFieldMetadata(catalog, index, p);
                builder.Add(fieldMetadata);
                index++;
            }
            this.Parameters = builder.ToImmutableList();
            this.IsOneWay   = thriftMethod.OneWay;
            this.Method     = method;
            this.Exceptions = BuildExceptions(catalog, method);
        }
        private static ThriftFieldMetadata CreateFieldMetadata(ThriftCatalog catalog, int index, ParameterInfo parameterInfo)
        {
            ThriftFieldAttribute thriftField   = parameterInfo.GetCustomAttribute <ThriftFieldAttribute>();
            short        parameterId           = short.MinValue;
            String       parameterName         = parameterInfo.Name;
            Requiredness parameterRequiredness = Requiredness.Unspecified;

            if (thriftField != null)
            {
                parameterId           = thriftField.Id;
                parameterRequiredness = thriftField.Required;
                if (!String.IsNullOrWhiteSpace(thriftField.Name))
                {
                    parameterName = thriftField.Name.Trim();
                }
            }
            if (parameterId == short.MinValue)
            {
                parameterId = (short)(index + 1);
            }
            ThriftType thriftType         = catalog.GetThriftType(parameterInfo.ParameterType);
            var        parameterInjection = new ThriftParameterInjection(parameterId, parameterName, index, parameterInfo.ParameterType);

            if (parameterRequiredness == Requiredness.Unspecified)
            {
                // There is only one field injection used to build metadata for method parameters, and if a
                // single injection point has UNSPECIFIED requiredness, that resolves to NONE.
                parameterRequiredness = Requiredness.None;
            }
            ThriftFieldMetadata fieldMetadata = new ThriftFieldMetadata(
                parameterId,
                false /* recursiveness */,
                parameterRequiredness,
                new DefaultThriftTypeReference(thriftType),
                parameterName,
                FieldKind.ThriftField,
                new IThriftInjection[] { parameterInjection });

            return(fieldMetadata);
        }