//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.Runtime.Serialization { using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Security; using System.Threading; using System.Xml; using DataContractDictionary = System.Collections.Generic.Dictionary; #if USE_REFEMIT public sealed class ClassDataContract : DataContract #else internal sealed class ClassDataContract : DataContract #endif { [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - XmlDictionaryString(s) representing the XML namespaces for class members." + "statically cached and used from IL generated code. should ideally be Critical." + "marked SecurityNode to be callable from transparent IL generated code." + "not changed to property to avoid regressing performance; any changes to initalization should be reviewed.")] public XmlDictionaryString[] ContractNamespaces; [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - XmlDictionaryString(s) representing the XML element names for class members." + "statically cached and used from IL generated code. should ideally be Critical." + "marked SecurityNode to be callable from transparent IL generated code." + "not changed to property to avoid regressing performance; any changes to initalization should be reviewed.")] public XmlDictionaryString[] MemberNames; [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - XmlDictionaryString(s) representing the XML namespaces for class members." + "statically cached and used from IL generated code. should ideally be Critical." + "marked SecurityNode to be callable from transparent IL generated code." + "not changed to property to avoid regressing performance; any changes to initalization should be reviewed.")] public XmlDictionaryString[] MemberNamespaces; [Fx.Tag.SecurityNote(Critical = "XmlDictionaryString representing the XML namespaces for members of class." + "Statically cached and used from IL generated code.")] #if !NO_SECURITY_ATTRIBUTES [SecurityCritical] #endif XmlDictionaryString[] childElementNamespaces; [Fx.Tag.SecurityNote(Critical = "Holds instance of CriticalHelper which keeps state that is cached statically for serialization. " + "Static fields are marked SecurityCritical or readonly to prevent data from being modified or leaked to other components in appdomain.")] #if !NO_SECURITY_ATTRIBUTES [SecurityCritical] #endif ClassDataContractCriticalHelper helper; [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'", Safe = "Doesn't leak anything.")] [SecuritySafeCritical] internal ClassDataContract() : base(new ClassDataContractCriticalHelper()) { InitClassDataContract(); } [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'", Safe = "Doesn't leak anything.")] #if !NO_SECURITY_ATTRIBUTES [SecuritySafeCritical] #endif internal ClassDataContract(Type type) : base(new ClassDataContractCriticalHelper(type)) { InitClassDataContract(); } [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical field 'helper'", Safe = "Doesn't leak anything.")] #if !NO_SECURITY_ATTRIBUTES [SecuritySafeCritical] #endif ClassDataContract(Type type, XmlDictionaryString ns, string[] memberNames) : base(new ClassDataContractCriticalHelper(type, ns, memberNames)) { InitClassDataContract(); } [Fx.Tag.SecurityNote(Critical = "Initializes SecurityCritical fields; called from all constructors.")] #if !NO_SECURITY_ATTRIBUTES [SecurityCritical] #endif void InitClassDataContract() { this.helper = base.Helper as ClassDataContractCriticalHelper; this.ContractNamespaces = helper.ContractNamespaces; this.MemberNames = helper.MemberNames; this.MemberNamespaces = helper.MemberNamespaces; } internal ClassDataContract BaseContract { [Fx.Tag.SecurityNote(Critical = "Fetches the critical baseContract property.", Safe = "baseContract only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.BaseContract; } [Fx.Tag.SecurityNote(Critical = "Sets the critical baseContract property.")] [SecurityCritical] set { helper.BaseContract = value; } } internal List Members { [Fx.Tag.SecurityNote(Critical = "Fetches the critical members property.", Safe = "members only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.Members; } [Fx.Tag.SecurityNote(Critical = "Sets the critical members property.", Safe = "Protected for write if contract has underlyingType.")] [SecurityCritical] set { helper.Members = value; } } public XmlDictionaryString[] ChildElementNamespaces { [Fx.Tag.SecurityNote(Critical = "Sets the critical childElementNamespaces property.", Safe = "childElementNamespaces only needs to be protected for write; initialized in getter if null.")] [SecuritySafeCritical] get { if (this.childElementNamespaces == null) { lock (this) { if (this.childElementNamespaces == null) { if (helper.ChildElementNamespaces == null) { XmlDictionaryString[] tempChildElementamespaces = CreateChildElementNamespaces(); Thread.MemoryBarrier(); helper.ChildElementNamespaces = tempChildElementamespaces; } this.childElementNamespaces = helper.ChildElementNamespaces; } } } return this.childElementNamespaces; } } internal MethodInfo OnSerializing { [Fx.Tag.SecurityNote(Critical = "Fetches the critical onSerializing property.", Safe = "onSerializing only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.OnSerializing; } } internal MethodInfo OnSerialized { [Fx.Tag.SecurityNote(Critical = "Fetches the critical onSerialized property.", Safe = "onSerialized only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.OnSerialized; } } internal MethodInfo OnDeserializing { [Fx.Tag.SecurityNote(Critical = "Fetches the critical onDeserializing property.", Safe = "onDeserializing only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.OnDeserializing; } } internal MethodInfo OnDeserialized { [Fx.Tag.SecurityNote(Critical = "Fetches the critical onDeserialized property.", Safe = "onDeserialized only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.OnDeserialized; } } internal MethodInfo ExtensionDataSetMethod { [Fx.Tag.SecurityNote(Critical = "Fetches the critical extensionDataSetMethod property.", Safe = "extensionDataSetMethod only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.ExtensionDataSetMethod; } } internal override DataContractDictionary KnownDataContracts { [Fx.Tag.SecurityNote(Critical = "Fetches the critical knownDataContracts property.", Safe = "knownDataContracts only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.KnownDataContracts; } [Fx.Tag.SecurityNote(Critical = "Sets the critical knownDataContracts property.", Safe = "Protected for write if contract has underlyingType.")] [SecurityCritical] set { helper.KnownDataContracts = value; } } internal override bool IsISerializable { [Fx.Tag.SecurityNote(Critical = "Fetches the critical isISerializable property.", Safe = "isISerializable only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.IsISerializable; } [Fx.Tag.SecurityNote(Critical = "Sets the critical isISerializable property.", Safe = "Protected for write if contract has underlyingType.")] [SecurityCritical] set { helper.IsISerializable = value; } } internal bool IsNonAttributedType { [Fx.Tag.SecurityNote(Critical = "Fetches the critical IsNonAttributedType property.", Safe = "IsNonAttributedType only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.IsNonAttributedType; } } internal bool HasDataContract { [Fx.Tag.SecurityNote(Critical = "Fetches the critical hasDataContract property.", Safe = "hasDataContract only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.HasDataContract; } } internal bool HasExtensionData { [Fx.Tag.SecurityNote(Critical = "Fetches the critical hasExtensionData property.", Safe = "hasExtensionData only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.HasExtensionData; } } internal string SerializationExceptionMessage { [Fx.Tag.SecurityNote(Critical = "Fetches the critical serializationExceptionMessage property.", Safe = "serializationExceptionMessage only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.SerializationExceptionMessage; } } internal string DeserializationExceptionMessage { [Fx.Tag.SecurityNote(Critical = "Fetches the critical deserializationExceptionMessage property.", Safe = "deserializationExceptionMessage only needs to be protected for write.")] [SecuritySafeCritical] get { return helper.DeserializationExceptionMessage; } } internal bool IsReadOnlyContract { get { return this.DeserializationExceptionMessage != null; } } [Fx.Tag.SecurityNote(Critical = "Fetches information about which constructor should be used to initialize ISerializable types.", Safe = "only needs to be protected for write.")] [SecuritySafeCritical] internal ConstructorInfo GetISerializableConstructor() { return helper.GetISerializableConstructor(); } [Fx.Tag.SecurityNote(Critical = "Fetches information about which constructor should be used to initialize non-attributed types that are valid for serialization.", Safe = "only needs to be protected for write.")] [SecuritySafeCritical] internal ConstructorInfo GetNonAttributedTypeConstructor() { return helper.GetNonAttributedTypeConstructor(); } internal XmlFormatClassWriterDelegate XmlFormatWriterDelegate { [Fx.Tag.SecurityNote(Critical = "Fetches the critical xmlFormatWriterDelegate property.", Safe = "xmlFormatWriterDelegate only needs to be protected for write; initialized in getter if null.")] [SecuritySafeCritical] get { if (helper.XmlFormatWriterDelegate == null) { lock (this) { if (helper.XmlFormatWriterDelegate == null) { XmlFormatClassWriterDelegate tempDelegate = new XmlFormatWriterGenerator().GenerateClassWriter(this); Thread.MemoryBarrier(); helper.XmlFormatWriterDelegate = tempDelegate; } } } return helper.XmlFormatWriterDelegate; } } internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate { [Fx.Tag.SecurityNote(Critical = "Fetches the critical xmlFormatReaderDelegate property.", Safe = "xmlFormatReaderDelegate only needs to be protected for write; initialized in getter if null.")] [SecuritySafeCritical] get { if (helper.XmlFormatReaderDelegate == null) { lock (this) { if (helper.XmlFormatReaderDelegate == null) { if (this.IsReadOnlyContract) { ThrowInvalidDataContractException(helper.DeserializationExceptionMessage, null /*type*/); } XmlFormatClassReaderDelegate tempDelegate = new XmlFormatReaderGenerator().GenerateClassReader(this); Thread.MemoryBarrier(); helper.XmlFormatReaderDelegate = tempDelegate; } } } return helper.XmlFormatReaderDelegate; } } internal static ClassDataContract CreateClassDataContractForKeyValue(Type type, XmlDictionaryString ns, string[] memberNames) { return new ClassDataContract(type, ns, memberNames); } internal static void CheckAndAddMember(List members, DataMember memberContract, Dictionary memberNamesTable) { DataMember existingMemberContract; if (memberNamesTable.TryGetValue(memberContract.Name, out existingMemberContract)) { Type declaringType = memberContract.MemberInfo.DeclaringType; DataContract.ThrowInvalidDataContractException( SR.GetString((declaringType.IsEnum ? SR.DupEnumMemberValue : SR.DupMemberName), existingMemberContract.MemberInfo.Name, memberContract.MemberInfo.Name, DataContract.GetClrTypeFullName(declaringType), memberContract.Name), declaringType); } memberNamesTable.Add(memberContract.Name, memberContract); members.Add(memberContract); } internal static XmlDictionaryString GetChildNamespaceToDeclare(DataContract dataContract, Type childType, XmlDictionary dictionary) { childType = DataContract.UnwrapNullableType(childType); if (!childType.IsEnum && !Globals.TypeOfIXmlSerializable.IsAssignableFrom(childType) && DataContract.GetBuiltInDataContract(childType) == null && childType != Globals.TypeOfDBNull) { string ns = DataContract.GetStableName(childType).Namespace; if (ns.Length > 0 && ns != dataContract.Namespace.Value) return dictionary.Add(ns); } return null; } [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - callers may need to depend on isNonAttributedType for a security decision." + "isNonAttributedType must be calculated correctly." + "IsNonAttributedTypeValidForSerialization is used as part of the isNonAttributedType calculation and is therefore marked with SecurityNote.", Safe = "Does not let caller influence isNonAttributedType calculation; no harm in leaking value.")] // check whether a corresponding update is required in DataContractCriticalHelper.CreateDataContract static internal bool IsNonAttributedTypeValidForSerialization(Type type) { if (type.IsArray) return false; if (type.IsEnum) return false; if (type.IsGenericParameter) return false; if (Globals.TypeOfIXmlSerializable.IsAssignableFrom(type)) return false; if (type.IsPointer) return false; if (type.IsDefined(Globals.TypeOfCollectionDataContractAttribute, false)) return false; Type[] interfaceTypes = type.GetInterfaces(); foreach (Type interfaceType in interfaceTypes) { if (CollectionDataContract.IsCollectionInterface(interfaceType)) return false; } if (type.IsSerializable) return false; if (Globals.TypeOfISerializable.IsAssignableFrom(type)) return false; if (type.IsDefined(Globals.TypeOfDataContractAttribute, false)) return false; if (type == Globals.TypeOfExtensionDataObject) return false; if (type.IsValueType) { return type.IsVisible; } else { return (type.IsVisible && type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Globals.EmptyTypeArray, null) != null); } } XmlDictionaryString[] CreateChildElementNamespaces() { if (Members == null) return null; XmlDictionaryString[] baseChildElementNamespaces = null; if (this.BaseContract != null) baseChildElementNamespaces = this.BaseContract.ChildElementNamespaces; int baseChildElementNamespaceCount = (baseChildElementNamespaces != null) ? baseChildElementNamespaces.Length : 0; XmlDictionaryString[] childElementNamespaces = new XmlDictionaryString[Members.Count + baseChildElementNamespaceCount]; if (baseChildElementNamespaceCount > 0) Array.Copy(baseChildElementNamespaces, 0, childElementNamespaces, 0, baseChildElementNamespaces.Length); XmlDictionary dictionary = new XmlDictionary(); for (int i = 0; i < this.Members.Count; i++) { childElementNamespaces[i + baseChildElementNamespaceCount] = GetChildNamespaceToDeclare(this, this.Members[i].MemberType, dictionary); } return childElementNamespaces; } [Fx.Tag.SecurityNote(Critical = "Calls critical method on helper.", Safe = "Doesn't leak anything.")] [SecuritySafeCritical] void EnsureMethodsImported() { helper.EnsureMethodsImported(); } public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context) { XmlFormatWriterDelegate(xmlWriter, obj, context, this); } public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context) { xmlReader.Read(); object o = XmlFormatReaderDelegate(xmlReader, context, MemberNames, MemberNamespaces); xmlReader.ReadEndElement(); return o; } #if !NO_DYNAMIC_CODEGEN [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - calculates whether this class requires MemberAccessPermission for deserialization." + "Since this information is used to determine whether to give the generated code access " + "permissions to private members, any changes to the logic should be reviewed.")] internal bool RequiresMemberAccessForRead(SecurityException securityException) { EnsureMethodsImported(); if (!IsTypeVisible(UnderlyingType)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractTypeNotPublic, DataContract.GetClrTypeFullName(UnderlyingType)), securityException)); } return true; } if (this.BaseContract != null && this.BaseContract.RequiresMemberAccessForRead(securityException)) return true; if (ConstructorRequiresMemberAccess(GetISerializableConstructor())) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustISerializableNoPublicConstructor, DataContract.GetClrTypeFullName(UnderlyingType)), securityException)); } return true; } if (ConstructorRequiresMemberAccess(GetNonAttributedTypeConstructor())) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustNonAttributedSerializableTypeNoPublicConstructor, DataContract.GetClrTypeFullName(UnderlyingType)), securityException)); } return true; } if (MethodRequiresMemberAccess(this.OnDeserializing)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractOnDeserializingNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), this.OnDeserializing.Name), securityException)); } return true; } if (MethodRequiresMemberAccess(this.OnDeserialized)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractOnDeserializedNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), this.OnDeserialized.Name), securityException)); } return true; } if (this.Members != null) { for (int i = 0; i < this.Members.Count; i++) { if (this.Members[i].RequiresMemberAccessForSet()) { if (securityException != null) { if (this.Members[i].MemberInfo is FieldInfo) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractFieldSetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), this.Members[i].MemberInfo.Name), securityException)); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractPropertySetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), this.Members[i].MemberInfo.Name), securityException)); } } return true; } } } return false; } [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - calculates whether this class requires MemberAccessPermission for serialization." + " Since this information is used to determine whether to give the generated code access" + " permissions to private members, any changes to the logic should be reviewed.")] internal bool RequiresMemberAccessForWrite(SecurityException securityException) { #if MONO_FEATURE_CAS EnsureMethodsImported(); if (!IsTypeVisible(UnderlyingType)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractTypeNotPublic, DataContract.GetClrTypeFullName(UnderlyingType)), securityException)); } return true; } if (this.BaseContract != null && this.BaseContract.RequiresMemberAccessForWrite(securityException)) return true; if (MethodRequiresMemberAccess(this.OnSerializing)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractOnSerializingNotPublic, DataContract.GetClrTypeFullName(this.UnderlyingType), this.OnSerializing.Name), securityException)); } return true; } if (MethodRequiresMemberAccess(this.OnSerialized)) { if (securityException != null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractOnSerializedNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), this.OnSerialized.Name), securityException)); } return true; } if (this.Members != null) { for (int i = 0; i < this.Members.Count; i++) { if (this.Members[i].RequiresMemberAccessForGet()) { if (securityException != null) { if (this.Members[i].MemberInfo is FieldInfo) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractFieldGetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), this.Members[i].MemberInfo.Name), securityException)); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new SecurityException(SR.GetString( SR.PartialTrustDataContractPropertyGetNotPublic, DataContract.GetClrTypeFullName(UnderlyingType), this.Members[i].MemberInfo.Name), securityException)); } } return true; } } } return false; #else return true; #endif } #endif [Fx.Tag.SecurityNote(Critical = "Holds all state used for (de)serializing classes." + " Since the data is cached statically, we lock down access to it.")] #if !NO_SECURITY_ATTRIBUTES [SecurityCritical(SecurityCriticalScope.Everything)] #endif class ClassDataContractCriticalHelper : DataContract.DataContractCriticalHelper { ClassDataContract baseContract; List members; MethodInfo onSerializing, onSerialized; MethodInfo onDeserializing, onDeserialized; MethodInfo extensionDataSetMethod; DataContractDictionary knownDataContracts; string serializationExceptionMessage; bool isISerializable; bool isKnownTypeAttributeChecked; bool isMethodChecked; bool hasExtensionData; [Fx.Tag.SecurityNote(Miscellaneous = "in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and hasDataContract.")] bool isNonAttributedType; [Fx.Tag.SecurityNote(Miscellaneous = "in serialization/deserialization we base the decision whether to Demand SerializationFormatter permission on this value and isNonAttributedType.")] bool hasDataContract; XmlDictionaryString[] childElementNamespaces; XmlFormatClassReaderDelegate xmlFormatReaderDelegate; XmlFormatClassWriterDelegate xmlFormatWriterDelegate; public XmlDictionaryString[] ContractNamespaces; public XmlDictionaryString[] MemberNames; public XmlDictionaryString[] MemberNamespaces; internal ClassDataContractCriticalHelper() : base() { } internal ClassDataContractCriticalHelper(Type type) : base(type) { XmlQualifiedName stableName = GetStableNameAndSetHasDataContract(type); if (type == Globals.TypeOfDBNull) { this.StableName = stableName; this.members = new List(); XmlDictionary dictionary = new XmlDictionary(2); this.Name = dictionary.Add(StableName.Name); this.Namespace = dictionary.Add(StableName.Namespace); this.ContractNamespaces = this.MemberNames = this.MemberNamespaces = new XmlDictionaryString[] { }; EnsureMethodsImported(); return; } Type baseType = type.BaseType; this.isISerializable = (Globals.TypeOfISerializable.IsAssignableFrom(type)); SetIsNonAttributedType(type); if (this.isISerializable) { if (HasDataContract) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.ISerializableCannotHaveDataContract, DataContract.GetClrTypeFullName(type)))); if (baseType != null && !(baseType.IsSerializable && Globals.TypeOfISerializable.IsAssignableFrom(baseType))) baseType = null; } this.IsValueType = type.IsValueType; if (baseType != null && baseType != Globals.TypeOfObject && baseType != Globals.TypeOfValueType && baseType != Globals.TypeOfUri) { DataContract baseContract = DataContract.GetDataContract(baseType); if (baseContract is CollectionDataContract) this.BaseContract = ((CollectionDataContract)baseContract).SharedTypeContract as ClassDataContract; else this.BaseContract = baseContract as ClassDataContract; if (this.BaseContract != null && this.BaseContract.IsNonAttributedType && !this.isNonAttributedType) { throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError (new InvalidDataContractException(SR.GetString(SR.AttributedTypesCannotInheritFromNonAttributedSerializableTypes, DataContract.GetClrTypeFullName(type), DataContract.GetClrTypeFullName(baseType)))); } } else this.BaseContract = null; hasExtensionData = (Globals.TypeOfIExtensibleDataObject.IsAssignableFrom(type)); if (hasExtensionData && !HasDataContract && !IsNonAttributedType) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.OnlyDataContractTypesCanHaveExtensionData, DataContract.GetClrTypeFullName(type)))); if (this.isISerializable) SetDataContractName(stableName); else { this.StableName = stableName; ImportDataMembers(); XmlDictionary dictionary = new XmlDictionary(2 + Members.Count); Name = dictionary.Add(StableName.Name); Namespace = dictionary.Add(StableName.Namespace); int baseMemberCount = 0; int baseContractCount = 0; if (BaseContract == null) { MemberNames = new XmlDictionaryString[Members.Count]; MemberNamespaces = new XmlDictionaryString[Members.Count]; ContractNamespaces = new XmlDictionaryString[1]; } else { if (BaseContract.IsReadOnlyContract) { this.serializationExceptionMessage = BaseContract.SerializationExceptionMessage; } baseMemberCount = BaseContract.MemberNames.Length; MemberNames = new XmlDictionaryString[Members.Count + baseMemberCount]; Array.Copy(BaseContract.MemberNames, MemberNames, baseMemberCount); MemberNamespaces = new XmlDictionaryString[Members.Count + baseMemberCount]; Array.Copy(BaseContract.MemberNamespaces, MemberNamespaces, baseMemberCount); baseContractCount = BaseContract.ContractNamespaces.Length; ContractNamespaces = new XmlDictionaryString[1 + baseContractCount]; Array.Copy(BaseContract.ContractNamespaces, ContractNamespaces, baseContractCount); } ContractNamespaces[baseContractCount] = Namespace; for (int i = 0; i < Members.Count; i++) { MemberNames[i + baseMemberCount] = dictionary.Add(Members[i].Name); MemberNamespaces[i + baseMemberCount] = Namespace; } } EnsureMethodsImported(); } internal ClassDataContractCriticalHelper(Type type, XmlDictionaryString ns, string[] memberNames) : base(type) { this.StableName = new XmlQualifiedName(GetStableNameAndSetHasDataContract(type).Name, ns.Value); ImportDataMembers(); XmlDictionary dictionary = new XmlDictionary(1 + Members.Count); Name = dictionary.Add(StableName.Name); Namespace = ns; ContractNamespaces = new XmlDictionaryString[] { Namespace }; MemberNames = new XmlDictionaryString[Members.Count]; MemberNamespaces = new XmlDictionaryString[Members.Count]; for (int i = 0; i < Members.Count; i++) { Members[i].Name = memberNames[i]; MemberNames[i] = dictionary.Add(Members[i].Name); MemberNamespaces[i] = Namespace; } EnsureMethodsImported(); } void EnsureIsReferenceImported(Type type) { DataContractAttribute dataContractAttribute; bool isReference = false; bool hasDataContractAttribute = TryGetDCAttribute(type, out dataContractAttribute); if (BaseContract != null) { if (hasDataContractAttribute && dataContractAttribute.IsReferenceSetExplicitly) { bool baseIsReference = this.BaseContract.IsReference; if ((baseIsReference && !dataContractAttribute.IsReference) || (!baseIsReference && dataContractAttribute.IsReference)) { DataContract.ThrowInvalidDataContractException( SR.GetString(SR.InconsistentIsReference, DataContract.GetClrTypeFullName(type), dataContractAttribute.IsReference, DataContract.GetClrTypeFullName(this.BaseContract.UnderlyingType), this.BaseContract.IsReference), type); } else { isReference = dataContractAttribute.IsReference; } } else { isReference = this.BaseContract.IsReference; } } else if (hasDataContractAttribute) { if (dataContractAttribute.IsReference) isReference = dataContractAttribute.IsReference; } if (isReference && type.IsValueType) { DataContract.ThrowInvalidDataContractException( SR.GetString(SR.ValueTypeCannotHaveIsReference, DataContract.GetClrTypeFullName(type), true, false), type); return; } this.IsReference = isReference; } void ImportDataMembers() { Type type = this.UnderlyingType; EnsureIsReferenceImported(type); List tempMembers = new List(); Dictionary memberNamesTable = new Dictionary(); MemberInfo[] memberInfos; if (this.isNonAttributedType) { memberInfos = type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); } else { memberInfos = type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } for (int i = 0; i < memberInfos.Length; i++) { MemberInfo member = memberInfos[i]; if (HasDataContract) { object[] memberAttributes = member.GetCustomAttributes(typeof(DataMemberAttribute), false); if (memberAttributes != null && memberAttributes.Length > 0) { if (memberAttributes.Length > 1) ThrowInvalidDataContractException(SR.GetString(SR.TooManyDataMembers, DataContract.GetClrTypeFullName(member.DeclaringType), member.Name)); DataMember memberContract = new DataMember(member); if (member.MemberType == MemberTypes.Property) { PropertyInfo property = (PropertyInfo)member; MethodInfo getMethod = property.GetGetMethod(true); if (getMethod != null && IsMethodOverriding(getMethod)) continue; MethodInfo setMethod = property.GetSetMethod(true); if (setMethod != null && IsMethodOverriding(setMethod)) continue; if (getMethod == null) ThrowInvalidDataContractException(SR.GetString(SR.NoGetMethodForProperty, property.DeclaringType, property.Name)); if (setMethod == null) { if (!SetIfGetOnlyCollection(memberContract, skipIfReadOnlyContract: false)) { this.serializationExceptionMessage = SR.GetString(SR.NoSetMethodForProperty, property.DeclaringType, property.Name); } } if (getMethod.GetParameters().Length > 0) ThrowInvalidDataContractException(SR.GetString(SR.IndexedPropertyCannotBeSerialized, property.DeclaringType, property.Name)); } else if (member.MemberType != MemberTypes.Field) ThrowInvalidDataContractException(SR.GetString(SR.InvalidMember, DataContract.GetClrTypeFullName(type), member.Name)); DataMemberAttribute memberAttribute = (DataMemberAttribute)memberAttributes[0]; if (memberAttribute.IsNameSetExplicitly) { if (memberAttribute.Name == null || memberAttribute.Name.Length == 0) ThrowInvalidDataContractException(SR.GetString(SR.InvalidDataMemberName, member.Name, DataContract.GetClrTypeFullName(type))); memberContract.Name = memberAttribute.Name; } else memberContract.Name = member.Name; memberContract.Name = DataContract.EncodeLocalName(memberContract.Name); memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType); memberContract.IsRequired = memberAttribute.IsRequired; if (memberAttribute.IsRequired && this.IsReference) { ThrowInvalidDataContractException( SR.GetString(SR.IsRequiredDataMemberOnIsReferenceDataContractType, DataContract.GetClrTypeFullName(member.DeclaringType), member.Name, true), type); } memberContract.EmitDefaultValue = memberAttribute.EmitDefaultValue; memberContract.Order = memberAttribute.Order; CheckAndAddMember(tempMembers, memberContract, memberNamesTable); } } else if (this.isNonAttributedType) { FieldInfo field = member as FieldInfo; PropertyInfo property = member as PropertyInfo; if ((field == null && property == null) || (field != null && field.IsInitOnly)) continue; object[] memberAttributes = member.GetCustomAttributes(typeof(IgnoreDataMemberAttribute), false); if (memberAttributes != null && memberAttributes.Length > 0) { if (memberAttributes.Length > 1) ThrowInvalidDataContractException(SR.GetString(SR.TooManyIgnoreDataMemberAttributes, DataContract.GetClrTypeFullName(member.DeclaringType), member.Name)); else continue; } DataMember memberContract = new DataMember(member); if (property != null) { MethodInfo getMethod = property.GetGetMethod(); if (getMethod == null || IsMethodOverriding(getMethod) || getMethod.GetParameters().Length > 0) continue; MethodInfo setMethod = property.GetSetMethod(true); if (setMethod == null) { // if the collection doesn't have the 'Add' method, we will skip it, for compatibility with 4.0 if (!SetIfGetOnlyCollection(memberContract, skipIfReadOnlyContract: true)) continue; } else { if (!setMethod.IsPublic || IsMethodOverriding(setMethod)) continue; } //skip ExtensionData member of type ExtensionDataObject if IExtensibleDataObject is implemented in non-attributed type if (this.hasExtensionData && memberContract.MemberType == Globals.TypeOfExtensionDataObject && member.Name == Globals.ExtensionDataObjectPropertyName) continue; } memberContract.Name = DataContract.EncodeLocalName(member.Name); memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType); CheckAndAddMember(tempMembers, memberContract, memberNamesTable); } else { FieldInfo field = member as FieldInfo; if (field != null && !field.IsNotSerialized) { DataMember memberContract = new DataMember(member); memberContract.Name = DataContract.EncodeLocalName(member.Name); object[] optionalFields = field.GetCustomAttributes(Globals.TypeOfOptionalFieldAttribute, false); if (optionalFields == null || optionalFields.Length == 0) { if (this.IsReference) { ThrowInvalidDataContractException( SR.GetString(SR.NonOptionalFieldMemberOnIsReferenceSerializableType, DataContract.GetClrTypeFullName(member.DeclaringType), member.Name, true), type); } memberContract.IsRequired = true; } memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType); CheckAndAddMember(tempMembers, memberContract, memberNamesTable); } } } if (tempMembers.Count > 1) tempMembers.Sort(DataMemberComparer.Singleton); SetIfMembersHaveConflict(tempMembers); Thread.MemoryBarrier(); members = tempMembers; } bool SetIfGetOnlyCollection(DataMember memberContract, bool skipIfReadOnlyContract) { //OK to call IsCollection here since the use of surrogated collection types is not supported in get-only scenarios if (CollectionDataContract.IsCollection(memberContract.MemberType, false /*isConstructorRequired*/, skipIfReadOnlyContract) && !memberContract.MemberType.IsValueType) { memberContract.IsGetOnlyCollection = true; return true; } return false; } void SetIfMembersHaveConflict(List members) { if (BaseContract == null) return; int baseTypeIndex = 0; List membersInHierarchy = new List(); foreach (DataMember member in members) { membersInHierarchy.Add(new Member(member, this.StableName.Namespace, baseTypeIndex)); } ClassDataContract currContract = BaseContract; while (currContract != null) { baseTypeIndex++; foreach (DataMember member in currContract.Members) { membersInHierarchy.Add(new Member(member, currContract.StableName.Namespace, baseTypeIndex)); } currContract = currContract.BaseContract; } IComparer comparer = DataMemberConflictComparer.Singleton; membersInHierarchy.Sort(comparer); for (int i = 0; i < membersInHierarchy.Count - 1; i++) { int startIndex = i; int endIndex = i; bool hasConflictingType = false; while (endIndex < membersInHierarchy.Count - 1 && String.CompareOrdinal(membersInHierarchy[endIndex].member.Name, membersInHierarchy[endIndex + 1].member.Name) == 0 && String.CompareOrdinal(membersInHierarchy[endIndex].ns, membersInHierarchy[endIndex + 1].ns) == 0) { membersInHierarchy[endIndex].member.ConflictingMember = membersInHierarchy[endIndex + 1].member; if (!hasConflictingType) { if (membersInHierarchy[endIndex + 1].member.HasConflictingNameAndType) { hasConflictingType = true; } else { hasConflictingType = (membersInHierarchy[endIndex].member.MemberType != membersInHierarchy[endIndex + 1].member.MemberType); } } endIndex++; } if (hasConflictingType) { for (int j = startIndex; j <= endIndex; j++) { membersInHierarchy[j].member.HasConflictingNameAndType = true; } } i = endIndex + 1; } } [Fx.Tag.SecurityNote(Critical = "Sets the critical hasDataContract field.", Safe = "Uses a trusted critical API (DataContract.GetStableName) to calculate the value, does not accept the value from the caller.")] [SecuritySafeCritical] XmlQualifiedName GetStableNameAndSetHasDataContract(Type type) { return DataContract.GetStableName(type, out this.hasDataContract); } [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - callers may need to depend on isNonAttributedType for a security decision." + "isNonAttributedType must be calculated correctly." + "SetIsNonAttributedType should not be called before GetStableNameAndSetHasDataContract since it is dependent on the correct calculation of hasDataContract.", Safe = "Does not let caller influence isNonAttributedType calculation; no harm in leaking value.")] void SetIsNonAttributedType(Type type) { this.isNonAttributedType = !type.IsSerializable && !this.hasDataContract && IsNonAttributedTypeValidForSerialization(type); } static bool IsMethodOverriding(MethodInfo method) { return method.IsVirtual && ((method.Attributes & MethodAttributes.NewSlot) == 0); } internal void EnsureMethodsImported() { if (!isMethodChecked && UnderlyingType != null) { lock (this) { if (!isMethodChecked) { Type type = this.UnderlyingType; MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < methods.Length; i++) { MethodInfo method = methods[i]; Type prevAttributeType = null; ParameterInfo[] parameters = method.GetParameters(); if (HasExtensionData && IsValidExtensionDataSetMethod(method, parameters)) { if (method.Name == Globals.ExtensionDataSetExplicitMethod || !method.IsPublic) extensionDataSetMethod = XmlFormatGeneratorStatics.ExtensionDataSetExplicitMethodInfo; else extensionDataSetMethod = method; } if (IsValidCallback(method, parameters, Globals.TypeOfOnSerializingAttribute, onSerializing, ref prevAttributeType)) onSerializing = method; if (IsValidCallback(method, parameters, Globals.TypeOfOnSerializedAttribute, onSerialized, ref prevAttributeType)) onSerialized = method; if (IsValidCallback(method, parameters, Globals.TypeOfOnDeserializingAttribute, onDeserializing, ref prevAttributeType)) onDeserializing = method; if (IsValidCallback(method, parameters, Globals.TypeOfOnDeserializedAttribute, onDeserialized, ref prevAttributeType)) onDeserialized = method; } Thread.MemoryBarrier(); isMethodChecked = true; } } } } bool IsValidExtensionDataSetMethod(MethodInfo method, ParameterInfo[] parameters) { if (method.Name == Globals.ExtensionDataSetExplicitMethod || method.Name == Globals.ExtensionDataSetMethod) { if (extensionDataSetMethod != null) ThrowInvalidDataContractException(SR.GetString(SR.DuplicateExtensionDataSetMethod, method, extensionDataSetMethod, DataContract.GetClrTypeFullName(method.DeclaringType))); if (method.ReturnType != Globals.TypeOfVoid) DataContract.ThrowInvalidDataContractException(SR.GetString(SR.ExtensionDataSetMustReturnVoid, DataContract.GetClrTypeFullName(method.DeclaringType), method), method.DeclaringType); if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != Globals.TypeOfExtensionDataObject) DataContract.ThrowInvalidDataContractException(SR.GetString(SR.ExtensionDataSetParameterInvalid, DataContract.GetClrTypeFullName(method.DeclaringType), method, Globals.TypeOfExtensionDataObject), method.DeclaringType); return true; } return false; } static bool IsValidCallback(MethodInfo method, ParameterInfo[] parameters, Type attributeType, MethodInfo currentCallback, ref Type prevAttributeType) { if (method.IsDefined(attributeType, false)) { if (currentCallback != null) DataContract.ThrowInvalidDataContractException(SR.GetString(SR.DuplicateCallback, method, currentCallback, DataContract.GetClrTypeFullName(method.DeclaringType), attributeType), method.DeclaringType); else if (prevAttributeType != null) DataContract.ThrowInvalidDataContractException(SR.GetString(SR.DuplicateAttribute, prevAttributeType, attributeType, DataContract.GetClrTypeFullName(method.DeclaringType), method), method.DeclaringType); else if (method.IsVirtual) DataContract.ThrowInvalidDataContractException(SR.GetString(SR.CallbacksCannotBeVirtualMethods, method, DataContract.GetClrTypeFullName(method.DeclaringType), attributeType), method.DeclaringType); else { if (method.ReturnType != Globals.TypeOfVoid) DataContract.ThrowInvalidDataContractException(SR.GetString(SR.CallbackMustReturnVoid, DataContract.GetClrTypeFullName(method.DeclaringType), method), method.DeclaringType); if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != Globals.TypeOfStreamingContext) DataContract.ThrowInvalidDataContractException(SR.GetString(SR.CallbackParameterInvalid, DataContract.GetClrTypeFullName(method.DeclaringType), method, Globals.TypeOfStreamingContext), method.DeclaringType); prevAttributeType = attributeType; } return true; } return false; } internal ClassDataContract BaseContract { get { return baseContract; } set { baseContract = value; if (baseContract != null && IsValueType) ThrowInvalidDataContractException(SR.GetString(SR.ValueTypeCannotHaveBaseType, StableName.Name, StableName.Namespace, baseContract.StableName.Name, baseContract.StableName.Namespace)); } } internal List Members { get { return members; } set { members = value; } } internal MethodInfo OnSerializing { get { EnsureMethodsImported(); return onSerializing; } } internal MethodInfo OnSerialized { get { EnsureMethodsImported(); return onSerialized; } } internal MethodInfo OnDeserializing { get { EnsureMethodsImported(); return onDeserializing; } } internal MethodInfo OnDeserialized { get { EnsureMethodsImported(); return onDeserialized; } } internal MethodInfo ExtensionDataSetMethod { get { EnsureMethodsImported(); return extensionDataSetMethod; } } internal override DataContractDictionary KnownDataContracts { get { if (!isKnownTypeAttributeChecked && UnderlyingType != null) { lock (this) { if (!isKnownTypeAttributeChecked) { knownDataContracts = DataContract.ImportKnownTypeAttributes(this.UnderlyingType); Thread.MemoryBarrier(); isKnownTypeAttributeChecked = true; } } } return knownDataContracts; } set { knownDataContracts = value; } } internal string SerializationExceptionMessage { get { return serializationExceptionMessage; } } internal string DeserializationExceptionMessage { get { if (serializationExceptionMessage == null) { return null; } else { return SR.GetString(SR.ReadOnlyClassDeserialization, this.serializationExceptionMessage); } } } internal override bool IsISerializable { get { return isISerializable; } set { isISerializable = value; } } internal bool HasDataContract { get { return hasDataContract; } } internal bool HasExtensionData { get { return hasExtensionData; } } internal bool IsNonAttributedType { get { return isNonAttributedType; } } internal ConstructorInfo GetISerializableConstructor() { if (!IsISerializable) return null; ConstructorInfo ctor = UnderlyingType.GetConstructor(Globals.ScanAllMembers, null, SerInfoCtorArgs, null); if (ctor == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(XmlObjectSerializer.CreateSerializationException(SR.GetString(SR.SerializationInfo_ConstructorNotFound, DataContract.GetClrTypeFullName(UnderlyingType)))); return ctor; } internal ConstructorInfo GetNonAttributedTypeConstructor() { if (!this.IsNonAttributedType) return null; Type type = UnderlyingType; if (type.IsValueType) return null; ConstructorInfo ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Globals.EmptyTypeArray, null); if (ctor == null) throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(SR.GetString(SR.NonAttributedSerializableTypesMustHaveDefaultConstructor, DataContract.GetClrTypeFullName(type)))); return ctor; } internal XmlFormatClassWriterDelegate XmlFormatWriterDelegate { get { return xmlFormatWriterDelegate; } set { xmlFormatWriterDelegate = value; } } internal XmlFormatClassReaderDelegate XmlFormatReaderDelegate { get { return xmlFormatReaderDelegate; } set { xmlFormatReaderDelegate = value; } } public XmlDictionaryString[] ChildElementNamespaces { get { return childElementNamespaces; } set { childElementNamespaces = value; } } static Type[] serInfoCtorArgs; static Type[] SerInfoCtorArgs { get { if (serInfoCtorArgs == null) serInfoCtorArgs = new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }; return serInfoCtorArgs; } } internal struct Member { internal Member(DataMember member, string ns, int baseTypeIndex) { this.member = member; this.ns = ns; this.baseTypeIndex = baseTypeIndex; } internal DataMember member; internal string ns; internal int baseTypeIndex; } internal class DataMemberConflictComparer : IComparer { public int Compare(Member x, Member y) { int nsCompare = String.CompareOrdinal(x.ns, y.ns); if (nsCompare != 0) return nsCompare; int nameCompare = String.CompareOrdinal(x.member.Name, y.member.Name); if (nameCompare != 0) return nameCompare; return x.baseTypeIndex - y.baseTypeIndex; } internal static DataMemberConflictComparer Singleton = new DataMemberConflictComparer(); } } [Fx.Tag.SecurityNote(Critical = "Sets critical properties on ClassDataContract .", Safe = "Called during schema import/code generation.")] [SecuritySafeCritical] internal override DataContract BindGenericParameters(DataContract[] paramContracts, Dictionary boundContracts) { Type type = UnderlyingType; if (!type.IsGenericType || !type.ContainsGenericParameters) return this; lock (this) { DataContract boundContract; if (boundContracts.TryGetValue(this, out boundContract)) return boundContract; ClassDataContract boundClassContract = new ClassDataContract(); boundContracts.Add(this, boundClassContract); XmlQualifiedName stableName; object[] genericParams; if (type.IsGenericTypeDefinition) { stableName = this.StableName; genericParams = paramContracts; } else { //partial Generic: Construct stable name from its open generic type definition stableName = DataContract.GetStableName(type.GetGenericTypeDefinition()); Type[] paramTypes = type.GetGenericArguments(); genericParams = new object[paramTypes.Length]; for (int i = 0; i < paramTypes.Length; i++) { Type paramType = paramTypes[i]; if (paramType.IsGenericParameter) genericParams[i] = paramContracts[paramType.GenericParameterPosition]; else genericParams[i] = paramType; } } boundClassContract.StableName = CreateQualifiedName(DataContract.ExpandGenericParameters(XmlConvert.DecodeName(stableName.Name), new GenericNameProvider(DataContract.GetClrTypeFullName(this.UnderlyingType), genericParams)), stableName.Namespace); if (BaseContract != null) boundClassContract.BaseContract = (ClassDataContract)BaseContract.BindGenericParameters(paramContracts, boundContracts); boundClassContract.IsISerializable = this.IsISerializable; boundClassContract.IsValueType = this.IsValueType; boundClassContract.IsReference = this.IsReference; if (Members != null) { boundClassContract.Members = new List(Members.Count); foreach (DataMember member in Members) boundClassContract.Members.Add(member.BindGenericParameters(paramContracts, boundContracts)); } return boundClassContract; } } internal override bool Equals(object other, Dictionary checkedContracts) { if (IsEqualOrChecked(other, checkedContracts)) return true; if (base.Equals(other, checkedContracts)) { ClassDataContract dataContract = other as ClassDataContract; if (dataContract != null) { if (IsISerializable) { if (!dataContract.IsISerializable) return false; } else { if (dataContract.IsISerializable) return false; if (Members == null) { if (dataContract.Members != null) { // check that all the datamembers in dataContract.Members are optional if (!IsEveryDataMemberOptional(dataContract.Members)) return false; } } else if (dataContract.Members == null) { // check that all the datamembers in Members are optional if (!IsEveryDataMemberOptional(Members)) return false; } else { Dictionary membersDictionary = new Dictionary(Members.Count); List dataContractMembersList = new List(); for (int i = 0; i < Members.Count; i++) { membersDictionary.Add(Members[i].Name, Members[i]); } for (int i = 0; i < dataContract.Members.Count; i++) { // check that all datamembers common to both datacontracts match DataMember dataMember; if (membersDictionary.TryGetValue(dataContract.Members[i].Name, out dataMember)) { if (dataMember.Equals(dataContract.Members[i], checkedContracts)) { membersDictionary.Remove(dataMember.Name); } else { return false; } } // otherwise save the non-matching datamembers for later verification else { dataContractMembersList.Add(dataContract.Members[i]); } } // check that datamembers left over from either datacontract are optional if (!IsEveryDataMemberOptional(membersDictionary.Values)) return false; if (!IsEveryDataMemberOptional(dataContractMembersList)) return false; } } if (BaseContract == null) return (dataContract.BaseContract == null); else if (dataContract.BaseContract == null) return false; else return BaseContract.Equals(dataContract.BaseContract, checkedContracts); } } return false; } bool IsEveryDataMemberOptional(IEnumerable dataMembers) { foreach (DataMember dataMember in dataMembers) { if (dataMember.IsRequired) return false; } return true; } public override int GetHashCode() { return base.GetHashCode(); } internal class DataMemberComparer : IComparer { public int Compare(DataMember x, DataMember y) { int orderCompare = x.Order - y.Order; if (orderCompare != 0) return orderCompare; return String.CompareOrdinal(x.Name, y.Name); } internal static DataMemberComparer Singleton = new DataMemberComparer(); } } }