public object Read(object value, ProtoReader source)
        {
			object[] values = new object[members.Length];
        	int tupleKey = 0;
        	object oldTuple = null;

        	bool issueReferenceDirectives = !baseTupleAsReference && asReference;

        	if (issueReferenceDirectives)
			{
				tupleKey = (int)source.ReadUInt32();

				if (tupleKey > 0)
				{
					return source.NetCache.GetKeyedObject(tupleKey);
				}
				else
				{
					bool dummy;
					oldTuple = new object();

					tupleKey = source.NetCache.AddObjectKey(oldTuple, out dummy);
				}
			}

			bool invokeCtor = false;
			if (value == null)
			{
				invokeCtor = true;
			}
			for (int i = 0; i < values.Length; i++)
				values[i] = GetValue(value, i);

        	int j = 0;
			
        	int field;
			while (j++ < values.Length && source.ReadNextFieldHack() > 0)
			{
				field = source.ReadFieldHeader();

				invokeCtor = true;
                if(field <= tails.Length)
                {
                    IProtoSerializer tail = tails[field - 1];
                    values[field - 1] = tails[field - 1].Read(tail.RequiresOldValue ? values[field - 1] : null, source);
                }
                else
                {
                    source.SkipField();
                }
            }

        	object result = invokeCtor ? ctor.Invoke(values) : value;

			if (issueReferenceDirectives)
			{
				source.NetCache.UpdateKeyedObject(tupleKey, oldTuple, result);
			}

			if (forceIssueFakeHeader)
			{
				source.ReadEndGroupFieldHeaderHack();
			}

        	return result;
        }