Создание метода расширения с помощью CodeDOM

Я пытаюсь создать метод расширения с помощью CodeDOM. Похоже, что для них нет никакой поддержки, и использование ExtensionAttribute (которое С# использует внутри для обозначения методов расширения) не разрешено.

Можно использовать трюк, чтобы указать модификатор this, но как сделать содержащий класс static, чтобы код на самом деле компилируется?

Поскольку static — это концепция C#, она не предоставляется через API CodeDOM. И установка TypeAttributes на TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.Public не работает, потому что

абстрактный класс не может быть запечатанным или статическим

Как заставить метод расширения компилироваться?


person svick    schedule 10.06.2011    source источник
comment
Будущие читатели: я думаю, что надежным решением теперь является roslyn и добавление статического модификатора к классу.   -  person user7116    schedule 12.04.2013


Ответы (4)


Я уверен, что вы ищете:

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

Однако, похоже, это не работает. Достаточно интересно:

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

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

Но все же, когда вы идете и выводите его, никаких изменений, хотя свойство Attributes явно изменяется с 0x00005002 на 0x00006003.

Для Microsoft Connect это невозможно:

Спасибо, что сообщили об этом. К сожалению, не похоже, что мы можем поддерживать статические классы для CodeDom.

Причина в том, что одной из целей разработки CodeDom является независимость от языка, чтобы любой код, сгенерированный для одного языка, можно было легко сгенерировать для другого языка. Хотя статические классы часто используются в C#, VB их не поддерживает. Таким образом, добавление поддержки статических классов будет означать, что некоторый код, который может компилироваться для C#, не будет компилироваться для VB, что противоречит нашим целям.

Хотя мы не можем решить эту проблему, мы просим вас продолжать оставлять отзывы в будущем, чтобы помочь нам стать лучше.


Грязный обходной путь:

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));

Производит:

#region
static
public class Extensions
{
}
#endregion

Который компилируется.

person user7116    schedule 10.06.2011
comment
Это, кажется, ничего не делает. И использование MemberAttributes.Private также не меняет класс на частный. - person svick; 10.06.2011
comment
@svick: странно, я подтвердил, что это так с 4.0. Проверка более старыми версиями компилятора. - person user7116; 10.06.2011
comment
@svick: обновлено, чтобы включить обходной путь с использованием CodeRegionDirectives. - person user7116; 10.06.2011
comment
Думаю, мне больше нравится мой обходной путь. Если у меня есть два плохих решения, и одно из них — «хитрый трюк», я предпочитаю более очевидное. - person svick; 10.06.2011
comment
@svick: согласен. Хотя вы всегда можете поместить мой трюк в метод расширения (о, ирония), чтобы сделать его более заметным. В любом случае, какая отягчающая находка. - person user7116; 10.06.2011
comment
Этот хак не работает, когда атрибуты должны быть помещены в статический класс. В моем случае мне пришлось отключить генерацию GeneratedCodeAttribute. Но спасибо за хороший лайфхак! - person GregC; 01.12.2011

Вместо прямой компиляции CodeCompileUnit вы можете получить исходный код, заменить class Extensions на static class Extensions и скомпилировать этот код.

person svick    schedule 10.06.2011
comment
Обратите внимание, что мне не нравится это решение, и я все еще ищу лучшее. - person svick; 10.06.2011
comment
Я обновил свой ответ, указав, почему вам придется пойти по этому пути. Я включил статью MS Connect, подробно описывающую то же самое. - person user7116; 10.06.2011
comment
Интересно, как вставить это в пространство имен как тип, так как это CodeSnippetTypeMember - person AymenDaoudi; 17.06.2021

Немного подправил хак, предоставленный sixlettervariables: поместил его в статический метод, как упоминалось в обсуждении.

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));
}
person GregC    schedule 01.12.2011

Вы можете заставить свой код компилироваться именно так, как вы этого хотите, преобразовав его непосредственно в строку, а затем взломав его:

    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"));
        }
    }
person Daryl    schedule 10.04.2017
comment
Да, это почти то, что я предложил в мом собственном ответе. - person svick; 10.04.2017