C#通过脚本实现接口的示例详解
作者:PascalMing
以前C#脚本用的委托注入模式,今天在提示下,尝试用脚本直接实现接口,然后C#可以动态或指定新类型创建接口实现对象,下面我们就来试一下吧
以前C#脚本用的委托注入模式,今天在提示下,尝试用脚本直接实现接口,然后C#可以动态或指定新类型创建接口实现对象。从代码角度看,稍显复杂,但脚本方面显得更简洁和有条理。
引用包需要Microsoft.CodeAnalysis、Microsoft.CodeAnalysis.Common等,其他自动添加:
<?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>
接口定义如下:
namespace WindowsFormsApp1 { public interface IFlexiblePluginAgent { string RegistDrType(); } }
验证文件TextFile1.txt如下:
using System; using System.Collections.Generic; public class FlexiblePluginAgentProcessScriptXXXX : WindowsFormsApp1.IFlexiblePluginAgent { public string RegistDrType() { return "scritp_drv"; } }
加载和验证代码如下:
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); } } }
到此这篇关于C#通过脚本实现接口的示例详解的文章就介绍到这了,更多相关C#接口内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!