internal static void Build() { if (readProps != null) { return; // completely built and ready for use } #if CF // no wait option on CF if (!Monitor.TryEnter(lockToken)) #else // give it a few seconds on regular .NET if (!Monitor.TryEnter(lockToken, 5000)) #endif { throw new InvalidOperationException("Possible deadlock detected preparing serializer for " + typeof(T).Name + "; try using Serializer.PrepareSerializer to initialize this type at application startup."); } try { if (readProps != null || subclasses != null) { // readProps != null : double-checked locking // readProps == null, but subclasses != null : // this scenario means that we are in the process of building the // serializer; since we hold the lock, this must be re-entrancy, so simply ignore //Trace.WriteLine("Re-entrant short-circuit: " + typeof(T).FullName, "ProtoBuf.Serializer:Build"); return; } //Trace.WriteLine("Building: " + typeof(T).FullName, "ProtoBuf.Serializer:Build"); // otherwise we are building the serializer for the first time; initialize // subclasses as a marker that we are in-progress subclasses = new KeyValuePair <Type, Property <T, T> > [0]; // use this to prevent recursion if (!Serializer.IsEntityType(typeof(T))) { throw new InvalidOperationException("Only data-contract classes can be processed (error processing " + typeof(T).Name + ")"); } BuildCallbacks(); List <Property <T> > readPropList = new List <Property <T> >(), writePropList = new List <Property <T> >(); List <int> tagsInUse = new List <int>(); foreach (MemberInfo prop in Serializer.GetProtoMembers(typeof(T))) { string name; DataFormat format; int tag; MemberSerializationOptions options; if (!Serializer.TryGetTag(prop, out tag, out name, out format, out options)) { continue; // didn't recognise this as a usable property } // check for duplicates if (tagsInUse.Contains(tag)) { throw new InvalidOperationException( string.Format("Duplicate tag {0} detected in {1}", tag, name)); } tagsInUse.Add(tag); Property <T> actualProp = PropertyFactory.Create <T>(prop); writePropList.Add(actualProp); readPropList.Add(actualProp); foreach (Property <T> altProp in actualProp.GetCompatibleReaders()) { if (altProp.Tag != actualProp.Tag) { throw new ProtoException("Alternative readers must handle the same tag"); } foreach (Property <T> tmp in readPropList) { if (tmp.FieldPrefix == altProp.FieldPrefix) { throw new ProtoException("Alternative readers must handle different wire-types"); } } readPropList.Add(altProp); } } #if CF2 CrapSort(readPropList, delegate(Property <T> x, Property <T> y) { return(x.FieldPrefix.CompareTo(y.FieldPrefix)); }); CrapSort(writePropList, delegate(Property <T> x, Property <T> y) { return(x.FieldPrefix.CompareTo(y.FieldPrefix)); }); #else readPropList.Sort(delegate(Property <T> x, Property <T> y) { return(x.FieldPrefix.CompareTo(y.FieldPrefix)); }); writePropList.Sort(delegate(Property <T> x, Property <T> y) { return(x.FieldPrefix.CompareTo(y.FieldPrefix)); }); #endif List <KeyValuePair <Type, Property <T, T> > > subclassList = new List <KeyValuePair <Type, Property <T, T> > >(); foreach (ProtoIncludeAttribute pia in Attribute.GetCustomAttributes(typeof(T), typeof(ProtoIncludeAttribute), false)) { Type subclassType = pia.ResolveKnownType(typeof(T).Assembly); if (subclassType == null) { throw new ProtoException("Unable to identify known-type for ProtoIncludeAttribute: " + pia.KnownTypeName); } if (subclassType.BaseType != typeof(T)) { throw new ProtoException(string.Format( "Known-type {0} for ProtoIncludeAttribute must be a direct subclass of {1}", subclassType.Name, typeof(T).Name)); } Property <T, T> prop; switch (pia.DataFormat) { case DataFormat.Default: prop = (Property <T, T>) PropertyUtil <T> .CreateTypedProperty("CreatePropertyMessageString", typeof(T), typeof(T), subclassType); break; case DataFormat.Group: prop = (Property <T, T>) PropertyUtil <T> .CreateTypedProperty("CreatePropertyMessageGroup", typeof(T), typeof(T), subclassType); break; default: throw new ProtoException("Invalid ProtoIncludeAttribute data-format: " + pia.DataFormat); } // check for duplicates if (tagsInUse.Contains(pia.Tag)) { throw new InvalidOperationException( string.Format("Duplicate tag {0} detected in sub-type {1}", pia.Tag, subclassType.Name)); } tagsInUse.Add(pia.Tag); prop.Init(pia.Tag, pia.DataFormat, PropertyFactory.GetPassThru <T>(), null, true, null); subclassList.Add(new KeyValuePair <Type, Property <T, T> >(subclassType, prop)); } subclasses = subclassList.ToArray(); writeProps = writePropList.ToArray(); readProps = readPropList.ToArray(); // this must be last; this lets other threads see the data } catch (Exception ex) { readProps = writeProps = null; subclasses = null; Debug.WriteLine("Build() failed for type: " + typeof(T).AssemblyQualifiedName); Debug.WriteLine(ex); throw; } finally { Monitor.Exit(lockToken); } }