开发者

Creating extension method using CodeDOM

I'm trying to create an extension method using CodeDOM. There doesn't seem to be any support for them and using ExtensionAttribute (which C# uses internally to mark extension methods) is not allowed.

It's possible to use a trick to specify the this modifier, but how do I make the containing class static, so that the code actually compiles?

Since static is a C# concept, it开发者_开发知识库's not exposed through the CodeDOM API. And setting TypeAttributes to TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.Public doesn't work, because

an abstract class cannot be sealed or static

How do I make the extension method to compile?


I'm pretty sure you're looking for:

var staticClass = new CodeTypeDeclaration("Extensions")
    {
        Attributes = MemberAttributes.Public|MemberAttributes.Static
    };

However, this appears not to work. Interestingly enough:

provider.Supports(GeneratorSupport.StaticConstructors);
// True

provider.Supports(GeneratorSupport.PublicStaticMembers);
// True

But yet when you go and output it, no changes even though the Attributes property clearly changes from 0x00005002 to 0x00006003.

Per Microsoft Connect this is not possible:

Thanks for reporting this. Unfortunately, it doesn't look like we can support static classes for CodeDom.

The reason is that one of the design goals of CodeDom is to be language-independent, so that any code generated for one language can easily be generated for a different language. While static classes are used often in C#, VB does not support them. Therefore, adding support for static classes will mean that some code that can compile for C# won't be compilable for VB, which goes against our goals.

While we can't act on this issue, we ask that you please continue to provide feedback in the future to help us improve.


A dirty workaround:

var type = new CodeTypeDeclaration("Extensions");
type.Attributes = MemberAttributes.Public;
type.StartDirectives.Add(
    new CodeRegionDirective(CodeRegionMode.Start, "\nstatic"));
type.EndDirectives.Add(
    new CodeRegionDirective(CodeRegionMode.End, String.Empty));

Produces:

#region
static
public class Extensions
{
}
#endregion

Which compiles.


Instead of compiling the CodeCompileUnit directly, you could get the source code, replace class Extensions with static class Extensions and compile that code.


Cleaned up the hack provided by sixlettervariables a little: placed it into a static method, as mentioned in the discussion.

public static void MarkAsStaticClassWithExtensionMethods(this CodeTypeDeclaration class_)
{
   class_.Attributes = MemberAttributes.Public;

   class_.StartDirectives.Add(new CodeRegionDirective(
           CodeRegionMode.Start, Environment.NewLine + "\tstatic"));

   class_.EndDirectives.Add(new CodeRegionDirective(
           CodeRegionMode.End, string.Empty));
}


You can get your code to compile exactly how your want it by converting it directly into a string, and then hacking it:

    private static CodeSnippetTypeMember CreateStaticClass(CodeTypeDeclaration type)
    {
        var provider = CodeDomProvider.CreateProvider("CSharp");
        using (var sourceWriter = new StringWriter())
        using (var tabbedWriter = new IndentedTextWriter(sourceWriter, "\t"))
        {
            tabbedWriter.Indent = 2;
            provider.GenerateCodeFromType(type, tabbedWriter, new CodeGeneratorOptions()
            {
                BracingStyle = "C",
                IndentString = "\t"
            });
            return new CodeSnippetTypeMember("\t\t" + sourceWriter.ToString().Replace("public class", "public static class"));
        }
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜