/// <summary> /// Creates a <see cref="CodeAttributeDeclaration"/> for a <see cref="DataContractAttribute"/> /// </summary> /// <param name="sourceType">The type to which the attribute will be applied (to use as a reference)</param> /// <param name="codeGenerator">The client proxy generator</param> /// <param name="referencingType">The type referencing this declaration</param> /// <returns>The new attribute declaration</returns> internal static CodeAttributeDeclaration CreateDataContractAttributeDeclaration(Type sourceType, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { CodeAttributeDeclaration dataContractAttrib = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.DataContractAttribute), codeGenerator, referencingType); string dataContractNamespace = CodeGenUtilities.GetContractNamespace(sourceType); string dataContractName = null; // If the user specified a DataContract, we should copy the namespace and name. DataContractAttribute sourceDataContractAttrib = (DataContractAttribute)Attribute.GetCustomAttribute(sourceType, typeof(DataContractAttribute)); if (sourceDataContractAttrib != null) { if (sourceDataContractAttrib.Namespace != null) { dataContractNamespace = sourceDataContractAttrib.Namespace; } if (sourceDataContractAttrib.Name != null) { dataContractName = sourceDataContractAttrib.Name; } } dataContractAttrib.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(dataContractNamespace))); if (dataContractName != null) { dataContractAttrib.Arguments.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(dataContractName))); } return(dataContractAttrib); }
private void GenerateContractMethodAttributes(CodeTypeDeclaration contractInterface, CodeMemberMethod beginQueryMethod, string domainServiceName, string operationName) { CodeAttributeDeclaration operationContractAtt = CodeGenUtilities.CreateAttributeDeclaration(typeof(OperationContractAttribute), this.ClientProxyGenerator, contractInterface); operationContractAtt.Arguments.Add(new CodeAttributeArgument("AsyncPattern", new CodePrimitiveExpression(true))); operationContractAtt.Arguments.Add(new CodeAttributeArgument("Action", new CodePrimitiveExpression(string.Format(CultureInfo.InvariantCulture, DomainServiceProxyGenerator.DefaultActionSchema, domainServiceName, operationName)))); operationContractAtt.Arguments.Add(new CodeAttributeArgument("ReplyAction", new CodePrimitiveExpression(string.Format(CultureInfo.InvariantCulture, DomainServiceProxyGenerator.DefaultReplyActionSchema, domainServiceName, operationName)))); beginQueryMethod.CustomAttributes.Add(operationContractAtt); }
/// <summary> /// Creates a <see cref="CodeAttributeDeclaration"/> for a <see cref="DisplayAttribute"/>. /// </summary> /// <param name="codeGenerator">The client proxy generator</param> /// <param name="referencingType">The type on whose member <see cref="DisplayAttribute"/> will be applied</param> /// <returns>The new attribute declaration</returns> internal static CodeAttributeDeclaration CreateDisplayAttributeDeclaration(CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { CodeAttributeDeclaration displayAttributeDeclaration = CodeGenUtilities.CreateAttributeDeclaration( typeof(DisplayAttribute), codeGenerator, referencingType); displayAttributeDeclaration.Arguments.Add(new CodeAttributeArgument("AutoGenerateField", new CodePrimitiveExpression(false))); return(displayAttributeDeclaration); }
private CodeTypeDeclaration GenerateContract(CodeTypeDeclaration proxyClass) { string domainServiceName = this._domainServiceDescription.DomainServiceType.Name; string contractTypeName = "I" + domainServiceName + "Contract"; CodeTypeDeclaration contractInterface = CodeGenUtilities.CreateTypeDeclaration(contractTypeName, proxyClass.UserData["Namespace"] as string); proxyClass.Members.Add(contractInterface); // Add <summary> xml comment to interface string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_DomainContext_ServiceContract_Summary_Comment, domainServiceName); contractInterface.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); contractInterface.IsInterface = true; contractInterface.CustomAttributes.Add( CodeGenUtilities.CreateAttributeDeclaration( typeof(ServiceContractAttribute), this.ClientProxyGenerator, proxyClass)); // Used to track types registered with ServiceKnownTypeAttribute (for Custom methods) HashSet <Type> registeredServiceTypes = new HashSet <Type>(); // Generate query methods, invoke operations and custom methods. foreach (DomainOperationEntry operation in this._domainServiceDescription.DomainOperationEntries .Where(op => op.Operation == DomainOperation.Query || op.Operation == DomainOperation.Invoke || op.Operation == DomainOperation.Custom) .OrderBy(op => op.Name)) { if (operation.Operation == DomainOperation.Custom) { this.GenerateContractServiceKnownTypes(contractInterface, operation, registeredServiceTypes); } else { this.GenerateContractMethod(contractInterface, operation); } } // Generate submit method if we have CUD operations. if (this._domainServiceDescription.DomainOperationEntries .Any(op => (op.Operation == DomainOperation.Delete || op.Operation == DomainOperation.Insert || op.Operation == DomainOperation.Update || op.Operation == DomainOperation.Custom))) { this.GenerateContractSubmitChangesMethod(contractInterface); } return(contractInterface); }
/// <summary> /// Creates an attribute declaration for <see cref="EnumMemberAttribute"/> /// </summary> /// <param name="memberInfo">The member that may contain an existing <see cref="EnumMemberAttribute"/></param> /// <param name="codeGenerator">The proxy generator</param> /// <param name="referencingType">The referencing type</param> /// <returns>A new attribute declaration</returns> internal static CodeAttributeDeclaration CreateEnumMemberAttributeDeclaration(MemberInfo memberInfo, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType) { CodeAttributeDeclaration enumMemberDecl = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.EnumMemberAttribute), codeGenerator, referencingType); // If the user specified a DataContract, we should copy the namespace and name. EnumMemberAttribute enumMemberAttrib = (EnumMemberAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(EnumMemberAttribute)); if (enumMemberAttrib != null) { string value = enumMemberAttrib.Value; if (!string.IsNullOrEmpty(value)) { enumMemberDecl.Arguments.Add(new CodeAttributeArgument("Value", new CodePrimitiveExpression(value))); } } return(enumMemberDecl); }
/// <summary> /// Creates a <see cref="CodeAttributeDeclaration"/> for the given <see cref="AttributeDeclaration"/>. /// </summary> /// <param name="proxyGenerator">The context for generating code. It cannot be null.</param> /// <param name="referencingType">The referencing type.</param> /// <param name="attributeDeclaration">The <see cref="AttributeDeclaration"/> to build.</param> /// <returns>A <see cref="CodeAttributeDeclaration"/>.</returns> private static CodeAttributeDeclaration CreateCodeAttributeDeclaration(CodeDomClientCodeGenerator proxyGenerator, CodeTypeDeclaration referencingType, AttributeDeclaration attributeDeclaration) { CodeAttributeDeclaration codeAttributeDeclaration = CodeGenUtilities.CreateAttributeDeclaration(attributeDeclaration.AttributeType, proxyGenerator, referencingType); // Add ctor args foreach (object arg in attributeDeclaration.ConstructorArguments) { CodeExpression expression = CreateCodeExpression(proxyGenerator, referencingType, arg); codeAttributeDeclaration.Arguments.Add(new CodeAttributeArgument(expression)); } // Add named params foreach (KeyValuePair <string, object> pair in attributeDeclaration.NamedParameters) { CodeExpression expression = CreateCodeExpression(proxyGenerator, referencingType, pair.Value); codeAttributeDeclaration.Arguments.Add(new CodeAttributeArgument(pair.Key, expression)); } return(codeAttributeDeclaration); }
/// <summary> /// Generates the client proxy code for the given type. /// </summary> public override void Generate() { // ---------------------------------------------------------------- // namespace // ---------------------------------------------------------------- CodeNamespace ns = this.ClientProxyGenerator.GetOrGenNamespace(this.Type); // Missing namespace bails out of code-gen -- error has been logged if (ns == null) { return; } // ---------------------------------------------------------------- // public partial class {Type} : (Base) // ---------------------------------------------------------------- this.ProxyClass = CodeGenUtilities.CreateTypeDeclaration(this.Type); this.ProxyClass.IsPartial = true; // makes this a partial type this.ProxyClass.TypeAttributes = TypeAttributes.Public; // Abstract classes must be preserved as abstract to avoid explicit instantiation on client bool isAbstract = (this.Type.IsAbstract); if (isAbstract) { this.ProxyClass.TypeAttributes |= TypeAttributes.Abstract; } // Determine all types derived from this one. // Note this list does not assume the current type is the visible root. That is a separate test. IEnumerable <Type> derivedTypes = this.GetDerivedTypes(); // If this type doesn't have any derivatives, seal it. Cannot seal abstracts. if (!isAbstract && !derivedTypes.Any()) { this.ProxyClass.TypeAttributes |= TypeAttributes.Sealed; } // Add all base types including interfaces this.AddBaseTypes(ns); ns.Types.Add(this.ProxyClass); AttributeCollection typeAttributes = this.Type.Attributes(); // Add <summary> xml comment to class string comment = this.GetSummaryComment(); this.ProxyClass.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // ---------------------------------------------------------------- // Add default ctr // ---------------------------------------------------------------- CodeConstructor constructor = new CodeConstructor(); // Default ctor is public for concrete types but protected for abstracts. // This prevents direct instantiation on client constructor.Attributes = isAbstract ? MemberAttributes.Family : MemberAttributes.Public; // add default ctor doc comments comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Default_Constructor_Summary_Comments, this.Type.Name); constructor.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // add call to default OnCreated method constructor.Statements.Add(this.NotificationMethodGen.OnCreatedMethodInvokeExpression); this.ProxyClass.Members.Add(constructor); // ---------------------------------------------------------------- // [KnownType(...), ...] // ---------------------------------------------------------------- // We need to generate a [KnownType] for all derived entities on the visible root. if (!this.IsDerivedType) { // Generate a [KnownType] for every derived type. // We specifically exclude [KnownTypes] from the set of attributes we ask // the metadata pipeline to generate below, meaning we take total control // here for which [KnownType] attributes get through the metadata pipeline. // // Note, we sort in alphabetic order to give predictability in baselines and // client readability. For cosmetic reasons, we sort by short or long name // depending on what our utility helpers will actually generated foreach (Type derivedType in derivedTypes.OrderBy(t => this.ClientProxyGenerator.ClientProxyCodeGenerationOptions.UseFullTypeNames ? t.FullName : t.Name)) { CodeAttributeDeclaration knownTypeAttrib = CodeGenUtilities.CreateAttributeDeclaration(typeof(System.Runtime.Serialization.KnownTypeAttribute), this.ClientProxyGenerator, this.ProxyClass); knownTypeAttrib.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(CodeGenUtilities.GetTypeReference(derivedType, this.ClientProxyGenerator, this.ProxyClass)))); this.ProxyClass.CustomAttributes.Add(knownTypeAttrib); } } this.ValidateTypeAttributes(typeAttributes); // ---------------------------------------------------------------- // [DataContract(Namespace=X, Name=Y)] // ---------------------------------------------------------------- CodeAttributeDeclaration dataContractAttrib = CodeGenUtilities.CreateDataContractAttributeDeclaration(this.Type, this.ClientProxyGenerator, this.ProxyClass); this.ProxyClass.CustomAttributes.Add(dataContractAttrib); // ---------------------------------------------------------------- // Propagate all type-level Attributes across (except DataContractAttribute since that is handled above) // ----------------------------------------------------------------- CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this.ProxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeType, ex.Message, this.ProxyClass.Name, ex.InnerException.Message), this.FilterTypeAttributes(typeAttributes), this.ProxyClass.CustomAttributes, this.ProxyClass.Comments); // ---------------------------------------------------------------- // gen proxy getter/setter for each property // ---------------------------------------------------------------- this.GenerateProperties(); // ---------------------------------------------------------------- // gen additional methods/events // ---------------------------------------------------------------- this.GenerateAdditionalMembers(); // Register created CodeTypeDeclaration with mapping this._typeMapping[this.Type] = this.ProxyClass; }
/// <summary> /// Generates a property getter/setter pair into the given proxy class to match the given property info. /// </summary> /// <param name="propertyDescriptor">PropertyDescriptor for the property to generate for.</param> protected virtual void GenerateProperty(PropertyDescriptor propertyDescriptor) { string propertyName = propertyDescriptor.Name; Type propertyType = CodeGenUtilities.TranslateType(propertyDescriptor.PropertyType); // ---------------------------------------------------------------- // Property type ref // ---------------------------------------------------------------- var propTypeReference = CodeGenUtilities.GetTypeReference(propertyType, this.ClientProxyGenerator, this.ProxyClass); // ---------------------------------------------------------------- // Property decl // ---------------------------------------------------------------- var property = new CodeMemberProperty(); property.Name = propertyName; property.Type = propTypeReference; property.Attributes = MemberAttributes.Public | MemberAttributes.Final; // final needed, else becomes virtual List <Attribute> propertyAttributes = propertyDescriptor.ExplicitAttributes().Cast <Attribute>().ToList(); // Generate <summary> for property string comment = string.Format(CultureInfo.CurrentCulture, Resource.CodeGen_Entity_Property_Summary_Comment, propertyName); property.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, this.ClientProxyGenerator.IsCSharp)); // ---------------------------------------------------------------- // [DataMember] -> Add if not already present. // ---------------------------------------------------------------- // Add if not already present. if (!propertyAttributes.OfType <DataMemberAttribute>().Any()) { CodeAttributeDeclaration dataMemberAtt = CodeGenUtilities.CreateAttributeDeclaration(typeof(DataMemberAttribute), this.ClientProxyGenerator, this.ProxyClass); property.CustomAttributes.Add(dataMemberAtt); } // Here, we check for the existence of [ReadOnly(true)] attributes generated when // the property does not not have a setter. We want to inject an [Editable(false)] // attribute into the pipeline. ReadOnlyAttribute readOnlyAttr = propertyAttributes.OfType <ReadOnlyAttribute>().SingleOrDefault(); if (readOnlyAttr != null && !propertyAttributes.OfType <EditableAttribute>().Any()) { propertyAttributes.Add(new EditableAttribute(!readOnlyAttr.IsReadOnly)); // REVIEW: should we strip out [ReadOnly] attributes here? } // Here we check if the type has a RoundtripOriginalAttribute. In that case we strip it out from the property if (this._isRoundtripType) { propertyAttributes.RemoveAll(attr => attr.GetType() == typeof(RoundtripOriginalAttribute)); } // Here we check for database generated fields. In that case we strip any RequiredAttribute from the property. if (propertyAttributes.Any(a => a.GetType().Name == "DatabaseGeneratedAttribute")) { propertyAttributes.RemoveAll(attr => attr.GetType() == typeof(RequiredAttribute)); } // Here, we check for the presence of a complex type. If it exists we need to add a DisplayAttribute // if not already there. DataSources windows do not handle complex types if (TypeUtility.IsSupportedComplexType(propertyType) && !propertyAttributes.OfType <DisplayAttribute>().Any()) { CodeAttributeDeclaration displayAttribute = CodeGenUtilities.CreateDisplayAttributeDeclaration(this.ClientProxyGenerator, this.ProxyClass); property.CustomAttributes.Add(displayAttribute); } // ---------------------------------------------------------------- // Propagate the custom attributes // ---------------------------------------------------------------- CustomAttributeGenerator.GenerateCustomAttributes( this.ClientProxyGenerator, this.ProxyClass, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, property.Name, this.ProxyClass.Name, ex.InnerException.Message), propertyAttributes.Cast <Attribute>(), property.CustomAttributes, property.Comments); // ---------------------------------------------------------------- // backing private field (CodeDom doesn't yet know about auto properties) // ---------------------------------------------------------------- string fieldName = CodeGenUtilities.MakeCompliantFieldName(propertyName); var field = new CodeMemberField(propTypeReference, fieldName); this.ProxyClass.Members.Add(field); var fieldRef = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName); var valueRef = new CodePropertySetValueReferenceExpression(); // ---------------------------------------------------------------- // getter body // ---------------------------------------------------------------- property.GetStatements.Add(new CodeMethodReturnStatement(fieldRef)); // ---------------------------------------------------------------- // setter body // ---------------------------------------------------------------- List <CodeStatement> bodyStatements = new List <CodeStatement>(); // this.OnPropertyXxxChanging(PropType value); bodyStatements.Add(this.NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changing")); bool propertyIsReadOnly = this.IsPropertyReadOnly(propertyDescriptor); if (!propertyIsReadOnly) { bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaiseDataMemberChanging", new CodePrimitiveExpression(propertyDescriptor.Name)))); } // Generate the validation tests. CodeStatement validationCode = GeneratePropertySetterValidation(propertyDescriptor.Name); bodyStatements.Add(validationCode); // this._field = value bodyStatements.Add(new CodeAssignStatement(fieldRef, valueRef)); if (!propertyIsReadOnly) { bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaiseDataMemberChanged", new CodePrimitiveExpression(propertyDescriptor.Name)))); } else { // even read-only members need to raise PropertyChanged bodyStatements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaisePropertyChanged", new CodePrimitiveExpression(propertyDescriptor.Name)))); } // this.OnPropertyXxxChanged(); bodyStatements.Add(this.NotificationMethodGen.GetMethodInvokeExpressionStatementFor(propertyName + "Changed")); // if (this._field != value)... CodeExpression valueTest = CodeGenUtilities.MakeNotEqual(propertyType, fieldRef, valueRef, this.ClientProxyGenerator.IsCSharp); CodeConditionStatement body = new CodeConditionStatement(valueTest, bodyStatements.ToArray <CodeStatement>()); property.SetStatements.Add(body); // add property this.ProxyClass.Members.Add(property); }
/// <summary> /// Creates a new <see cref="CodeTypeDeclaration"/> that is the generated form of /// the given <paramref name="enumType"/>. /// </summary> /// <param name="enumType">The enum type to generate.</param> /// <param name="codeGenerator">The current proxy generator context.</param> /// <returns>The newly generated enum type declaration.</returns> internal static CodeTypeDeclaration CreateEnumTypeDeclaration(Type enumType, CodeDomClientCodeGenerator codeGenerator) { System.Diagnostics.Debug.Assert(enumType.IsEnum, "Type must be an enum type"); CodeTypeDeclaration typeDecl = CodeGenUtilities.CreateTypeDeclaration(enumType); typeDecl.IsEnum = true; // Always force generated enums to be public typeDecl.TypeAttributes |= TypeAttributes.Public; // Enums deriving from anything but int get an explicit base type Type underlyingType = enumType.GetEnumUnderlyingType(); if (underlyingType != typeof(int)) { typeDecl.BaseTypes.Add(new CodeTypeReference(underlyingType)); } typeDecl.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment($"Enum {enumType.Name}", codeGenerator.IsCSharp)); // Generate [DataContract] if it appears in the original only. Use Reflection only because that matches // what WCF will do. DataContractAttribute dataContractAttr = (DataContractAttribute)Attribute.GetCustomAttribute(enumType, typeof(DataContractAttribute)); if (dataContractAttr != null) { CodeAttributeDeclaration attrDecl = CodeGenUtilities.CreateDataContractAttributeDeclaration(enumType, codeGenerator, typeDecl); typeDecl.CustomAttributes.Add(attrDecl); } string[] memberNames = Enum.GetNames(enumType); Type enumValueType = Enum.GetUnderlyingType(enumType); for (int i = 0; i < memberNames.Length; ++i) { string memberName = memberNames[i]; CodeTypeReference enumTypeRef = CodeGenUtilities.GetTypeReference(enumValueType, codeGenerator, typeDecl); CodeMemberField enumMember = new CodeMemberField(enumTypeRef, memberName); enumMember.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(memberName, codeGenerator.IsCSharp)); // Generate an initializer for the enum member. // GetRawConstantValue is the safest way to get the raw value of the enum field // and works for both Reflection and ReflectionOnly loaded assemblies. FieldInfo fieldInfo = enumType.GetField(memberName); if (fieldInfo != null) { object memberValue = fieldInfo.GetRawConstantValue(); Debug.Assert(memberValue != null, "Enum type's GetRawConstantValue should never return null"); // We special-case MinValue and MaxValue for the integral types // because VisualBasic will generate overflow compiler error for // Int64.MinValue. If we detect a known MinValue or MaxValue for // this integral type, we generate that reference, otherwise we // just generate a constant integral value of the enum's type object[] minMaxValues = null; CodeGenUtilities.integralMinMaxValues.TryGetValue(underlyingType, out minMaxValues); Debug.Assert(minMaxValues == null || minMaxValues.Length == 3, "integralMinMaxValues elements must always contain 3 values"); // Gen xxx.MinValue if it matches, but give precedence to matching a true zero, // which is the min value for the unsigned integral types // minMaxValues[0]: the MinValue for this type // minMaxValues[1]: the MaxValue for this type // minMaxValues[2]: the zero for this type (memberValue is not boxed and cannot be cast) if (minMaxValues != null && !memberValue.Equals(minMaxValues[2]) && memberValue.Equals(minMaxValues[0])) { enumMember.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(underlyingType), "MinValue"); } // Gen xxx.MaxValue if it matches else if (minMaxValues != null && memberValue.Equals(minMaxValues[1])) { enumMember.InitExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(underlyingType), "MaxValue"); } // All other cases generate an integral constant. // CodeDom knows how to generate the right integral constant based on memberValue's type. else { enumMember.InitExpression = new CodePrimitiveExpression(memberValue); } } typeDecl.Members.Add(enumMember); // Generate an [EnumMember] if appropriate EnumMemberAttribute enumMemberAttr = (EnumMemberAttribute)Attribute.GetCustomAttribute(fieldInfo, typeof(EnumMemberAttribute)); if (enumMemberAttr != null) { CodeAttributeDeclaration enumAttrDecl = CodeGenUtilities.CreateEnumMemberAttributeDeclaration(fieldInfo, codeGenerator, typeDecl); enumMember.CustomAttributes.Add(enumAttrDecl); } // Propagate any other attributes that can be seen by the client CustomAttributeGenerator.GenerateCustomAttributes( codeGenerator, typeDecl, ex => string.Format(CultureInfo.CurrentCulture, Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, ex.Message, fieldInfo.Name, typeDecl.Name, ex.InnerException.Message), fieldInfo.GetCustomAttributes(false).Cast <Attribute>().Where(a => a.GetType() != typeof(EnumMemberAttribute)), enumMember.CustomAttributes, enumMember.Comments); } // Attributes marked with [Flag] propagate it if (enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0) { CodeAttributeDeclaration attrDecl = CodeGenUtilities.CreateAttributeDeclaration(typeof(FlagsAttribute), codeGenerator, typeDecl); typeDecl.CustomAttributes.Add(attrDecl); } return(typeDecl); }