walterlv / BlogComments

3 stars 0 forks source link

post/compile-and-invoke-code-using-roslyn #7

Open utterances-bot opened 5 years ago

utterances-bot commented 5 years ago

Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码 - walterlv

Roslyn 入门:使用 .NET Core 版本的 Roslyn 编译并执行跨平台的静态的源码

https://blog.walterlv.com/post/compile-and-invoke-code-using-roslyn.html

zhusheping commented 5 years ago

CompilingException找不到。

walterlv commented 5 years ago

呀!不好意思 @zhusheping 忽略这个类型,你自己随便抛出一个自己的类型。这是我自己定义的异常类型,在 https://github.com/dotnet-campus/SourceFusion 项目中定义的。

zhusheping commented 5 years ago

谢谢啦。 从你的https://blog.walterlv.com上面学到了不少知识。最近项目上要用到Roslyn动态执行,在使用中有问题,能否请教你?

比如有个类: public class Test { public string Say(string s) { Console.WriteLine(s);return s; } public T Get(string s) { return default(T); } public T Get(string s,int i) { return default(T); } public string Say(string s, int i) { Console.WriteLine($"{s}:{i}");return $"{s}:{i}"; } }

动态脚本里面是这几句话 var test = New Test(); var str = test.Say("hello"); int i = test.Get("get");

我如何用CSharpSyntaxRewriter 来动态的改动让他们执行:

var test = New Test(); var str = test.Say("hello",1); int i = test.Get("get",1);

这样做看起来没什么意义,但只是为了简化问题,实际上是有应用场景的。

麻烦大神了。

Deep Mind Solutions. We Help .Solutions for Printing Industry

Name:朱社平

Mob: +8618993794536 zhusp@deepmind.cn Guangzhou, China

------------------ 原始邮件 ------------------ 发件人: "walterlv"notifications@github.com; 发送时间: 2019年4月28日(星期天) 上午8:39 收件人: "walterlv/BlogComments"BlogComments@noreply.github.com; 抄送: "阿平"790978@qq.com; "Mention"mention@noreply.github.com; 主题: Re: [walterlv/BlogComments] post/compile-and-invoke-code-using-roslyn(#7)

呀!不好意思 @zhusheping 忽略这个类型,你自己随便抛出一个自己的类型。这是我自己定义的异常类型,在 dotnet-campus/SourceFusion: SourceFusion is a pre-compile framework based on Roslyn. It helps you to build high-performance .NET code. 项目中定义的。

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

walterlv commented 5 years ago

@zhusheping 如果你是希望运行时动态执行,建议考虑 Emit 或者表达式树:

如果是编译期间动态改变,其实这么简单的,只需要字符串(正则)匹配一下就好了。

如果够复杂,需要使用 Roslyn 语法重写的话,你需要 override 查找 InvocationExpression 的方法,然后依次找 InvocationExpressionArgumentList,然后调用 Update 修改此 ArgumentList

关于找什么的问题,可以阅读:

zhusheping commented 5 years ago

感谢回复。 我找到了部分实现代码。

        string originalText = System.IO.File.ReadAllText(@"C:\Users\yalin\Desktop\aaa.cs");
        var syntaxTree = CSharpSyntaxTree.ParseText(originalText);

        var syntaxRoot = syntaxTree.GetCompilationUnitRoot();
        var invocationExpressions = syntaxRoot.DescendantNodes().OfType<InvocationExpressionSyntax>();
        foreach (var item in invocationExpressions)
        {
            var argus =  item.ArgumentList.AddArguments(
                SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(2)))
                );
            var ss = item.Update(item, argus);
        }

只是不知道如何更新回ArgumentList。我试过update,但好像不行。这个语法上不太好试出来。

walterlv commented 5 years ago

@zhusheping Roslyn 的语法树是 Immutable 不可变的,你调用 Update 方法后不会修改源树,而是会生成一个新树。所以你需要将新树存下来输出成文本才可以。

shibox commented 5 years ago

CSharpSyntaxTree.ParseText(code) 这个方法执行一次就增加10M的内存,如果大量调用,GC量很大,性能很是个问题,可以怎么优化下,内存少占一些吗

walterlv commented 5 years ago

@shibox 所以,不要总是通过 ParseText 来解析一段文本,这样确实每次都会创建新的语法节点,建议使用工作区 Workspace 来分析。

Roslyn 中使用红绿树来尽可能复用生成的语法节点,见下文。此复用在工作区范围生效。如果你每次都解析文本将不会复用。

下面是使用工作区来分析代码的例子:

另外,如果在写 Visual Studio 的插件,那么 Roslyn API 的实现已经做好了复用语法节点,只需要重写就好了。

zhenlei520 commented 2 years ago

public class GenericGenerator { private static readonly string GeneratedAttribute = @"[System.CodeDom.Compiler.GeneratedCode(""walterlv"", ""1.0"")]";

public string Transform(string originalCode, int genericCount)
{
    if (genericCount == 1)
    {
        return originalCode;
    }

    var content = originalCode
        // 替换泛型。
        .Replace("<out T>", FromTemplate("<{0}>", "out T{n}", ", ", genericCount))
        .Replace("Task<T>", FromTemplate("Task<({0})>", "T{n}", ", ", genericCount))
        .Replace("Func<T, Task>", FromTemplate("Func<{0}, Task>", "T{n}", ", ", genericCount))
        .Replace(" T, Task>", FromTemplate(" {0}, Task>", "T{n}", ", ", genericCount))
        .Replace("(T, bool", FromTemplate("({0}, bool", "T{n}", ", ", genericCount))
        .Replace("var (t, ", FromTemplate("var ({0}, ", "t{n}", ", ", genericCount))
        .Replace(", t)", FromTemplate(", {0})", "t{n}", ", ", genericCount))
        .Replace("return (t, ", FromTemplate("return ({0}, ", "t{n}", ", ", genericCount))
        .Replace("<T>", FromTemplate("<{0}>", "T{n}", ", ", genericCount))
        .Replace("(T value)", FromTemplate("(({0}) value)", "T{n}", ", ", genericCount))
        .Replace("(T t)", FromTemplate("({0})", "T{n} t{n}", ", ", genericCount))
        .Replace("(t)", FromTemplate("({0})", "t{n}", ", ", genericCount))
        .Replace("var t =", FromTemplate("var ({0}) =", "t{n}", ", ", genericCount))
        .Replace(" T ", FromTemplate(" ({0}) ", "T{n}", ", ", genericCount))
        .Replace(" t;", FromTemplate(" ({0});", "t{n}", ", ", genericCount))
        // 生成 [GeneratedCode]。
        .Replace("    public interface ", $"    {GeneratedAttribute}{System.Environment.NewLine}    public interface ")
        .Replace("    public class ", $"    {GeneratedAttribute}{System.Environment.NewLine}    public class ")
        .Replace("    public sealed class ", $"    {GeneratedAttribute}{System.Environment.NewLine}    public sealed class ");
    return content.Trim();
}

private static string FromTemplate(string template, string part, string seperator, int count)
{
    return string.Format(template,
        string.Join(seperator, Enumerable.Range(1, count).Select(x => part.Replace("{n}", x.ToString()))));
}

}

是seperator而不是separator

walterlv commented 2 years ago

@zhenlei520 非常感谢您帮助我指出博客里的问题!

正确的写法是 separator(我的所有博客里十多处都是这么写的),错误的写法是 seperator(我有两处这么写错了,其中一处被您指出了)。

zhenlei520 commented 2 years ago

separator

好吧 那可能是改博客的时候只改了调用而没改参数名