/// <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)); } // 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); // 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); }
/// <summary> /// Gets a <see cref="CodeTypeReference"/> for a CLR type. /// </summary> /// <param name="type">A CLR type.</param> /// <param name="codeGenerator">A <see cref="CodeDomClientCodeGenerator"/>.</param> /// <param name="referencingType">The referencing type.</param> /// <param name="optimizeAttributeName">Indicates whether or not to optimize <see cref="Attribute"/> names by removing the "Attribute" suffix.</param> /// <param name="forceUseFullyQualifiedName">Indicates whether or not to generate the type using the fully qualified name irrespective the global setting.</param> /// <returns>A <see cref="CodeTypeReference"/> for a CLR type.</returns> internal static CodeTypeReference GetTypeReference(Type type, CodeDomClientCodeGenerator codeGenerator, CodeTypeDeclaration referencingType, bool optimizeAttributeName, bool forceUseFullyQualifiedName) { string typeName = type.Name; string typeNamespace = type.Namespace; // Add an import statement to the referencing type if needed CodeNamespace ns = codeGenerator.GetNamespace(referencingType); CodeTypeReference codeTypeReference = null; // Attribute? If so, we special case these and remove the 'Attribute' suffix if present. if (optimizeAttributeName) { typeName = OptimizeAttributeName(type); } // Determine if we should generate this type with a full type name bool useFullyQualifiedName = forceUseFullyQualifiedName || CodeGenUtilities._useFullTypeNames || RegisterTypeName(typeNamespace, typeName, ns.Name); // Make sure we take into account root namespace in VB codegen. typeNamespace = TranslateNamespace(type, codeGenerator); // Conditionally add an import statement. Skip this step if we need to generate a full // type name, if we're already in the target namespace, or if the type is in the global namespace. if (!useFullyQualifiedName && !ns.Name.Equals(type.Namespace) && !string.IsNullOrEmpty(type.Namespace)) { // If the namespace is already imported, the following line will be a no-op. ns.Imports.Add(new CodeNamespaceImport(typeNamespace)); } // If forced using Fully Qualified names, dont look up or store the code reference in the cache. That is because, // we force the use of fully qualified names only in certain cases. Caching at this time will cause the fully qualified name // to be used every time. bool useCache = !forceUseFullyQualifiedName; // See if we already have a reference for this type Tuple <CodeNamespace, Type> tupleKey = new Tuple <CodeNamespace, Type>(ns, type); if (!useCache || !CodeGenUtilities._codeTypeReferences.TryGetValue(tupleKey, out codeTypeReference)) { if (useFullyQualifiedName && !string.IsNullOrEmpty(typeNamespace)) { // While this splicing may seem awkward, we perform this task // rather than rely on 'type.FullName' as we may have performed // a VB root namespace translation task above. typeName = typeNamespace + "." + typeName; } // If not, create a new type reference. Use the constructor for CodeTypeReference // that takes a type's name rather than type to generate short names. if (type.IsArray) { codeTypeReference = new CodeTypeReference( CodeGenUtilities.GetTypeReference(type.GetElementType(), codeGenerator, referencingType, /* optimizeAttributeName */ false, forceUseFullyQualifiedName), type.GetArrayRank()); } else if (type.IsGenericType) { Type[] genericArguments = type.GetGenericArguments(); CodeTypeReference[] typeArguments = new CodeTypeReference[genericArguments.Length]; for (int i = 0; i < genericArguments.Length; i++) { typeArguments[i] = GetTypeReference(genericArguments[i], codeGenerator, referencingType); } codeTypeReference = new CodeTypeReference(typeName, typeArguments); } else { // Generate language-specific shorthands for core types by using CodeTypeReference constructor that takes a Type if (type.IsPrimitive || type == typeof(void) || type == typeof(decimal) || type == typeof(string) || type == typeof(object)) { codeTypeReference = new CodeTypeReference(type); } else { codeTypeReference = new CodeTypeReference(typeName); } } // Keep track of the CLR type for identification purposes. codeTypeReference.UserData["ClrType"] = type; // Cache for later use. if (useCache) { CodeGenUtilities._codeTypeReferences.Add(tupleKey, codeTypeReference); } } return(codeTypeReference); }