C# 通过脚本实现接口

以前C#脚本用的委托注入模式,今天在AI提示下,尝试用脚本直接实现接口,然后C#可以动态或指定新类型创建接口实现对象。从代码角度看,稍显复杂,但脚本方面显得更简洁和有条理。

引用包需要Microsoft.CodeAnalysis、Microsoft.CodeAnalysis.Common等,其他自动添加:

cs 复制代码
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Humanizer.Core" version="2.14.1" targetFramework="net472" />
  <package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net472" />
  <package id="Microsoft.CodeAnalysis" version="4.13.0" targetFramework="net472" />
  <package id="Microsoft.CodeAnalysis.Analyzers" version="3.11.0" targetFramework="net472" developmentDependency="true" />
  <package id="Microsoft.CodeAnalysis.Common" version="4.13.0" targetFramework="net472" />
  <package id="Microsoft.CodeAnalysis.CSharp" version="4.13.0" targetFramework="net472" />
  <package id="Microsoft.CodeAnalysis.CSharp.Scripting" version="4.13.0" targetFramework="net472" />
  <package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="4.13.0" targetFramework="net472" />
  <package id="Microsoft.CodeAnalysis.Scripting.Common" version="4.13.0" targetFramework="net472" />
  <package id="Microsoft.CodeAnalysis.VisualBasic" version="4.13.0" targetFramework="net472" />
  <package id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="4.13.0" targetFramework="net472" />
  <package id="Microsoft.CodeAnalysis.Workspaces.Common" version="4.13.0" targetFramework="net472" />
  <package id="Microsoft.CSharp" version="4.7.0" targetFramework="net472" />
  <package id="System.Buffers" version="4.5.1" targetFramework="net472" />
  <package id="System.Collections.Immutable" version="8.0.0" targetFramework="net472" />
  <package id="System.Composition" version="8.0.0" targetFramework="net472" />
  <package id="System.Composition.AttributedModel" version="8.0.0" targetFramework="net472" />
  <package id="System.Composition.Convention" version="8.0.0" targetFramework="net472" />
  <package id="System.Composition.Hosting" version="8.0.0" targetFramework="net472" />
  <package id="System.Composition.Runtime" version="8.0.0" targetFramework="net472" />
  <package id="System.Composition.TypedParts" version="8.0.0" targetFramework="net472" />
  <package id="System.IO.Pipelines" version="8.0.0" targetFramework="net472" />
  <package id="System.Memory" version="4.5.5" targetFramework="net472" />
  <package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
  <package id="System.Reflection.Metadata" version="8.0.0" targetFramework="net472" />
  <package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net472" />
  <package id="System.Text.Encoding.CodePages" version="7.0.0" targetFramework="net472" />
  <package id="System.Threading.Channels" version="7.0.0" targetFramework="net472" />
  <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net472" />
</packages>

接口定义如下:

cs 复制代码
namespace WindowsFormsApp1
{
    public interface IFlexiblePluginAgent
    {
        string RegistDrType();
    }
}

验证文件TextFile1.txt如下:

cs 复制代码
using System;
using System.Collections.Generic;

public class FlexiblePluginAgentProcessScriptXXXX : WindowsFormsApp1.IFlexiblePluginAgent
{
	public string RegistDrType()
	{
		return "scritp_drv";
	}
}

加载和验证代码如下:

cs 复制代码
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace WindowsFormsApp1
{
    internal static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            ScriptTest();
        }
        static void ScriptTest()
        {
            try
            {
                // 读取外部代码文件
                string codeFilePath = "TextFile1.txt";
                string sourceCode = File.ReadAllText(codeFilePath);

                var compilation = CSharpCompilation.Create("DynamicAssembly")
                    .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
                    .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
                    .AddReferences(MetadataReference.CreateFromFile(typeof(IFlexiblePluginAgent).Assembly.Location))
                    .AddSyntaxTrees(CSharpSyntaxTree.ParseText(sourceCode));

                // 检查编译错误
                var diagnostics = compilation.GetDiagnostics();
                if (diagnostics.HasAnyErrors())
                {
                    Console.WriteLine("编译错误:");
                    foreach (var diagnostic in diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error))
                    {
                        Console.WriteLine(diagnostic.ToString());
                    }
                    return;
                }

                // 内存中生成程序集
                using (var ms = new MemoryStream())
                {
                    EmitResult emitResult = compilation.Emit(ms);

                    if (!emitResult.Success)
                    {
                        Console.WriteLine("程序集生成失败:");
                        foreach (var diagnostic in emitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error))
                        {
                            Console.WriteLine(diagnostic.ToString());
                        }
                        return;
                    }

                    ms.Seek(0, SeekOrigin.Begin);
                    Assembly assembly = Assembly.Load(ms.ToArray());

                    Console.WriteLine("---检查程序集中是否有IFlexiblePluginAgent的派生类---");
                    var derivedTypes = assembly.DefinedTypes
                        .Where(t => typeof(IFlexiblePluginAgent).IsAssignableFrom(t) && !t.IsInterface)
                        .ToList();

                    if (derivedTypes.Any())
                    {
                        Console.WriteLine("找到以下IFlexiblePluginAgent的派生类:");
                        foreach (var type in derivedTypes)
                        {
                            Console.WriteLine($"  - {type.FullName}");
                        }

                        // 使用第一个派生类创建对象
                        Type agentType = derivedTypes.First().AsType();
                        IFlexiblePluginAgent agent = (IFlexiblePluginAgent)Activator.CreateInstance(agentType);
                        string result = agent.RegistDrType();

                        Console.WriteLine($"注册的 DrType 是: {result}");
                    }
                    else
                    {
                        Console.WriteLine("未找到IFlexiblePluginAgent的派生类");
                    }

                    Console.WriteLine("---知道类名字直接调用---");
                    Type agentType2 = assembly.GetType("FlexiblePluginAgentProcessScriptXXXX");
                    if (agentType2 == null)
                    {
                        Console.WriteLine("未找到 MyPluginAgent 类型");
                        return;
                    }

                    IFlexiblePluginAgent agent2 = (IFlexiblePluginAgent)Activator.CreateInstance(agentType2);
                    string result2 = agent2.RegistDrType();

                    Console.WriteLine($"注册的 DrType 是: {result2}");

                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"发生错误: {ex.Message}");
            }
        }
    }
    // 扩展方法用于检查诊断信息
    public static class DiagnosticExtensions
    {
        public static bool HasAnyErrors(this IEnumerable<Diagnostic> diagnostics)
        {
            return diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error);
        }
    }
}

代码托管地址:GitHub - PascalMing/CodeAnalysisInterface: C#脚本实现接口并加载验证

相关推荐
我好喜欢你~4 小时前
C#---StopWatch类
开发语言·c#
一阵没来由的风8 小时前
拒绝造轮子(C#篇)ZLG CAN卡驱动封装应用
c#·can·封装·zlg·基础封装·轮子
一枚小小程序员哈14 小时前
基于微信小程序的家教服务平台的设计与实现/基于asp.net/c#的家教服务平台/基于asp.net/c#的家教管理系统
后端·c#·asp.net
Eternity_GQM16 小时前
【Word VBA Zotero 引用宏错误分析与改正指南】【解决[21–23]参考文献格式插入超链接问题】
开发语言·c#·word
cimeo20 小时前
【C 学习】06-算法&程序设计举例
c#
百锦再21 小时前
.NET 的 WebApi 项目必要可配置项都有哪些?
java·开发语言·c#·.net·core·net
WYH2871 天前
C#控制台输入(Read()、ReadKey()和ReadLine())
开发语言·c#
hqwest1 天前
C#WPF实战出真汁06--【系统设置】--餐桌类型设置
c#·.net·wpf·布局·分页·命令·viewmodel
做一位快乐的码农1 天前
基于.net、C#、asp.net、vs的保护大自然网站的设计与实现
c#·asp.net·.net
DavieLau1 天前
C#项目WCF接口暴露调用及SOAP接口请求测试(Python版)
xml·服务器·开发语言·python·c#