Avalonia配置,参考https://www.cnblogs.com/dalgleish/p/18967204
随时更新,目前已支持多个cs文件动态编译。
AvaloniaExtensions.cs代码
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Shapes;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.VisualTree;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Shares.Avalonia
{
public static class AvaloniaExtensions
{
public static IResourceDictionary WindowResources(this Control topLevel)
{
return Application.Current?.Resources!;
}
public static IClassicDesktopStyleApplicationLifetime? GetDesktopLifetime()
{
return Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
}
public static Control Create(this Control topLevel, string target, params object[] parameters)
{
var viewType = Type.GetType($"{target}, {target.Split(".")[0]}");
if (viewType == null)
return CreateErrorControl($"报错:无法找到类型 \"{target}\",请确保其已位于正确命名空间");
return (Control)Activator.CreateInstance(viewType, args: parameters)!;
}
// 缓存已编译代码的 Assembly
private static readonly ConcurrentDictionary<string, Assembly> assemblyCache = new();
//单文件编译
public static async Task<Control> RunXamlAsync(this Control topLevel, string? xaml, string? code, string cls = "SampleView")
{
if (string.IsNullOrWhiteSpace(xaml))
return CreateErrorControl("XAML代码为空,请提供有效的 XAML。");
string key = $"{xaml.GetHashCode()}_{code?.GetHashCode() ?? 0}_{cls}";
try
{
Assembly? scriptAssembly = null;
object? rootInstance = null;
if (!string.IsNullOrWhiteSpace(code))
{
if (!assemblyCache.TryGetValue(key, out scriptAssembly))
{
var (assembly, _) = await CompilerService.GetScriptAssembly(code);
if (assembly == null)
return CreateErrorControl("C# 代码编译失败,请检查语法。");
assemblyCache[key] = assembly;
scriptAssembly = assembly;
}
var sampleViewType = scriptAssembly.GetTypes()
.FirstOrDefault(t => t.Name == cls);
if (sampleViewType == null)
return CreateErrorControl($"在编译结果中未找到类型 \"{cls}\",请确保定义了该类。");
rootInstance = Activator.CreateInstance(sampleViewType);
}
Control? control;
if (scriptAssembly != null && rootInstance != null)
{
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
control = AvaloniaRuntimeXamlLoader.Load(stream, scriptAssembly, rootInstance) as Control;
}
else
{
control = AvaloniaRuntimeXamlLoader.Parse<Control?>(xaml);
}
if (control is Window w)
{
if (w.Content is string str)
control = new Label() { Content = str };
else
control = (Control)w.Content!;
}
return control ?? CreateErrorControl("XAML加载失败,返回了空控件。");
}
catch (Exception ex)
{
Console.WriteLine(ex);
return CreateErrorControl("发生异常:" + ex.Message);
}
}
//多文件编译
/*
* var codeFiles = new Dictionary<string, string>
{
["SampleView.cs"] = File.ReadAllText("SampleView.cs"),
["SampleViewModel.cs"] = File.ReadAllText("SampleViewModel.cs")
};
*/
public static async Task<Control> RunXamlAsync(this Control topLevel, string? xaml, Dictionary<string, string>? codeFiles, string cls = "SampleView")
{
if (string.IsNullOrWhiteSpace(xaml))
return CreateErrorControl("XAML代码为空,请提供有效的 XAML。");
string key = $"{xaml.GetHashCode()}_{(codeFiles is null ? 0 : string.Join(",", codeFiles.Keys).GetHashCode())}_{cls}";
try
{
Assembly? scriptAssembly = null;
object? rootInstance = null;
if (codeFiles is not null && codeFiles.Count > 0)
{
if (!assemblyCache.TryGetValue(key, out scriptAssembly))
{
var (assembly, _) = await CompilerService.GetScriptAssembly(codeFiles);
if (assembly == null)
return CreateErrorControl("C# 多文件代码编译失败,请检查语法。");
assemblyCache[key] = assembly;
scriptAssembly = assembly;
}
var sampleViewType = scriptAssembly.GetTypes()
.FirstOrDefault(t => t.Name == cls);
if (sampleViewType == null)
return CreateErrorControl($"在编译结果中未找到类型 \"{cls}\",请确保在源码中正确定义。");
rootInstance = Activator.CreateInstance(sampleViewType);
}
Control? control;
if (scriptAssembly != null && rootInstance != null)
{
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml));
control = AvaloniaRuntimeXamlLoader.Load(stream, scriptAssembly, rootInstance) as Control;
}
else
{
control = AvaloniaRuntimeXamlLoader.Parse<Control?>(xaml);
}
if (control is Window w)
{
if (w.Content is string str)
control = new Label() { Content = str };
else
control = (Control)w.Content!;
}
return control ?? CreateErrorControl("XAML加载失败,返回了空控件。");
}
catch (Exception ex)
{
Console.WriteLine(ex);
return CreateErrorControl("发生异常:" + ex.Message);
}
}
//不可用
public static async Task<Control> RunAvaresResourceAsync(this Control topLevel, string fileName, string? code = null, string cls = "SampleView")
{
var assembly = topLevel.GetType().Assembly;
var assemblyName = assembly.GetName().Name;
var baseUri = new Uri($"avares://{assemblyName}");
try
{
// 获取所有资源并匹配文件名
var allResources = AssetLoader.GetAssets(baseUri, null);
var resourceUri = allResources.FirstOrDefault(u =>
u.AbsolutePath.EndsWith(fileName, StringComparison.OrdinalIgnoreCase));
if (resourceUri == null)
{
var available = string.Join("\n", allResources.Select(u => u.AbsolutePath));
return CreateErrorControl($"找不到 {fileName}\n可用资源:\n{available}");
}
Stream resourceStream = AssetLoader.Open(resourceUri);
using var reader = new StreamReader(resourceStream);
var xaml = await reader.ReadToEndAsync();
return await topLevel.RunXamlAsync(xaml, code, cls);
}
catch (Exception ex)
{
return CreateErrorControl($"加载错误: {ex.Message}");
}
}
public static async Task<Control> RunResourceAsync(this Control topLevel, string fileName)
{
var assembly = topLevel.GetType().Assembly;
try
{
var path = $"{assembly.GetName().Name}.{fileName}";
// UI代码必须在主线程中执行
return await Dispatcher.UIThread.InvokeAsync(() =>
{
Control? control = topLevel.Create(path);
if (control is Window w)
{
if (w.Content is string str)
control = new Label() { Content = str };
else
control = (Control)w.Content!;
}
return control ?? CreateErrorControl("XAML加载失败,返回了空控件。");
});
}
catch (Exception ex)
{
return await Dispatcher.UIThread.InvokeAsync(() =>
CreateErrorControl("发生异常:" + ex.Message));
}
}
private static Control CreateErrorControl(string message)
{
return new Border
{
Background = Brushes.MistyRose,
BorderBrush = Brushes.IndianRed,
BorderThickness = new Thickness(1),
Padding = new Thickness(1),
Child = new TextBlock
{
Text = message,
Foreground = Brushes.DarkRed,
FontWeight = FontWeight.Bold,
TextWrapping = TextWrapping.Wrap,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
},
};
}
//RoutedEventArgs扩展
public static object? OriginalSource(this RoutedEventArgs e)
{
return GetDesktopLifetime()?.MainWindow;
}
//命令行
public static string[]? GetCommandLine(this Control topLevel)
{
return GetDesktopLifetime()?.Args;
}
//读取文件
public static async Task<string?> ReadAllTextAsync(this Control topLevel, string fileName)
{
var assembly = topLevel.GetType().Assembly;
var assemblyName = assembly.GetName().Name;
var baseUri = new Uri($"avares://{assemblyName}");
var allResources = AssetLoader.GetAssets(baseUri, null);
var resourceUri = allResources.FirstOrDefault(u =>
u.AbsolutePath.EndsWith(fileName, StringComparison.OrdinalIgnoreCase));
if (resourceUri == null)
return null;
Stream resourceStream = AssetLoader.Open(resourceUri);
using var reader = new StreamReader(resourceStream);
var content = await reader.ReadToEndAsync();
return content;
}
public static void SetZIndex(this Control topLevel, int index)
=> topLevel.SetValue(Panel.ZIndexProperty, index);
public static int GetZIndex(this Control topLevel)
=> topLevel.GetValue(Panel.ZIndexProperty);
public static T? GetService<T>()
{
var serviceType = typeof(T);
Console.WriteLine($"尝试获取服务类型:{serviceType.FullName}");
var locatorType = typeof(AvaloniaLocator);
PropertyInfo? currentProp = null;
Type? typeWalker = locatorType;
while (typeWalker != null)
{
currentProp = typeWalker.GetProperty("Current", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (currentProp != null)
{
Console.WriteLine("找到 AvaloniaLocator.Current 属性");
break;
}
typeWalker = typeWalker.BaseType;
}
if (currentProp == null)
{
Console.WriteLine("未找到 AvaloniaLocator.Current 属性");
return default;
}
var currentLocator = currentProp.GetValue(null);
if (currentLocator == null)
{
Console.WriteLine("AvaloniaLocator.Current 返回 null");
return default;
}
// 尝试调用泛型方法 GetService<T>()
var methods = currentLocator.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(m => m.Name == "GetService" && m.IsGenericMethod);
MethodInfo? genericMethod = methods.FirstOrDefault();
if (genericMethod != null)
{
try
{
var result = genericMethod.MakeGenericMethod(serviceType).Invoke(currentLocator, null);
if (result is T t)
{
Console.WriteLine($"通过泛型方法获取服务成功:{serviceType.FullName}");
return t;
}
else
{
Console.WriteLine($"泛型方法返回结果为空或类型不匹配");
}
}
catch (Exception ex)
{
Console.WriteLine($"调用泛型方法 GetService<T>() 异常:{ex.Message}");
}
}
// 回退:GetService(Type)
MethodInfo? getServiceByType = currentLocator.GetType().GetMethod("GetService", new[] { typeof(Type) });
if (getServiceByType != null)
{
try
{
var service = getServiceByType.Invoke(currentLocator, new object[] { serviceType });
if (service is T t2)
{
Console.WriteLine($"通过 GetService(Type) 获取服务成功:{serviceType.FullName}");
return t2;
}
else
{
Console.WriteLine($"GetService(Type) 返回结果为空或类型不匹配");
}
}
catch (Exception ex)
{
Console.WriteLine($"调用 GetService(Type) 异常:{ex.Message}");
}
}
// 回退实例或创建
try
{
var instanceProp = serviceType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (instanceProp != null)
{
var instance = instanceProp.GetValue(null);
if (instance is T typedInstance)
{
Console.WriteLine($"通过静态 Instance 属性获取服务成功:{serviceType.FullName}");
return typedInstance;
}
else
{
Console.WriteLine($"Instance 属性为空或类型不匹配");
}
}
if (!serviceType.IsAbstract)
{
var created = Activator.CreateInstance(serviceType);
if (created is T createdTyped)
{
Console.WriteLine($"通过 Activator.CreateInstance 创建服务成功:{serviceType.FullName}");
return createdTyped;
}
}
}
catch (Exception ex)
{
Console.WriteLine($"尝试通过 Instance 或 Activator 创建服务失败:{ex.Message}");
}
Console.WriteLine($"未能获取服务:{serviceType.FullName}");
return default;
}
//外接矩形
public static Rect GetMinBoundingMatrix(Visual visual, Visual relativeTo)
{
if (visual == null || relativeTo == null)
return new Rect();
var transform = visual.TransformToVisual(relativeTo);
if (transform is null)
return new Rect();
var bounds = visual.Bounds;
// 四个角点(本地坐标)
var points = new[]
{
new Point(0, 0),
new Point(bounds.Width, 0),
new Point(bounds.Width, bounds.Height),
new Point(0, bounds.Height)
};
// 应用变换
var transformed = points.Select(p => transform.Value.Transform(p)).ToArray();
// 计算外接矩形
var minX = transformed.Min(p => p.X);
var maxX = transformed.Max(p => p.X);
var minY = transformed.Min(p => p.Y);
var maxY = transformed.Max(p => p.Y);
return new Rect(minX, minY, maxX - minX, maxY - minY);
}
//递归应用矩阵变化
public static TransformedBounds? GetTransformedBounds(Visual visual, Visual relativeTo)
{
if (visual == null || relativeTo == null)
return null;
Rect clip = new Rect();
var transform = Matrix.Identity;
bool Visit(Visual v)
{
if (!v.IsVisible)
return false;
var bounds = new Rect(v.Bounds.Size);
if (v == relativeTo)
{
clip = bounds;
transform = Matrix.Identity;
return true;
}
var parent = v.GetVisualParent();
if (parent == null)
return false;
if (!Visit(parent))
return false;
// 计算当前节点的渲染变换矩阵
var renderTransform = Matrix.Identity;
if (v.HasMirrorTransform)
{
var mirrorMatrix = new Matrix(-1.0, 0.0, 0.0, 1.0, v.Bounds.Width, 0);
renderTransform *= mirrorMatrix;
}
if (v.RenderTransform != null)
{
var origin = v.RenderTransformOrigin.ToPixels(bounds.Size);
var offset = Matrix.CreateTranslation(origin);
var finalTransform = (-offset) * v.RenderTransform.Value * offset;
renderTransform *= finalTransform;
}
// 叠加变换矩阵
transform = renderTransform *
Matrix.CreateTranslation(v.Bounds.Position) *
transform;
// 处理剪裁
if (v.ClipToBounds)
{
var globalBounds = bounds.TransformToAABB(transform);
var clipBounds = v.ClipToBounds ? globalBounds.Intersect(clip) : clip;
clip = clip.Intersect(clipBounds);
}
return true;
}
return Visit(visual) ? new TransformedBounds(new Rect(visual.Bounds.Size), clip, transform) : null;
}
//获取所有command
public static List<ICommand> GetICommands(this Control topLevel)
{
var commands = new HashSet<ICommand>();
// 1. 先查 DataContext 中的所有 ICommand 属性
if (topLevel.DataContext != null)
{
var props = topLevel.DataContext.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => typeof(ICommand).IsAssignableFrom(p.PropertyType));
foreach (var prop in props)
{
if (prop.GetValue(topLevel.DataContext) is ICommand cmd)
commands.Add(cmd);
}
}
// 2. 再查控件自身的 ICommandSource.Command
if (topLevel is ICommandSource commandSource && commandSource.Command != null)
{
commands.Add(commandSource.Command);
}
// 3. 子控件递归
foreach (var child in topLevel.GetVisualChildren())
{
if (child is Control c)
commands.UnionWith(GetICommands(c));
}
return commands.ToList();
}
//在控件树及其 DataContext 中按名称查找所有 ICommand(允许省略 "Command" 后缀,去重)
public static List<ICommand> GetICommands(this Control topLevel, string name)
{
var commands = new HashSet<ICommand>();
string fullName = name.EndsWith("Command") ? name : name + "Command";
// 当前控件:DataContext
if (topLevel.DataContext != null)
{
var prop = topLevel.DataContext.GetType().GetProperty(fullName, BindingFlags.Public | BindingFlags.Instance);
if (prop != null && typeof(ICommand).IsAssignableFrom(prop.PropertyType))
{
if (prop.GetValue(topLevel.DataContext) is ICommand cmd)
commands.Add(cmd);
}
}
// 当前控件:ICommandSource
if (topLevel is ICommandSource commandSource && commandSource.Command != null)
{
if (topLevel.Name == name || topLevel.Name == fullName ||
commandSource.Command.GetType().Name == name || commandSource.Command.GetType().Name == fullName)
commands.Add(commandSource.Command);
}
// 递归子控件
foreach (var child in topLevel.GetVisualChildren())
{
if (child is Control c)
commands.UnionWith(GetICommands(c, name)); // 递归并去重
}
return commands.ToList();
}
public static ICommand? GetICommand(this Control topLevel, string name)
{
// 自动补 "Command" 后缀
string fullName = name.EndsWith("Command") ? name : name + "Command";
// 1. 当前控件:检查 DataContext 是否有这个命令
if (topLevel.DataContext != null)
{
var prop = topLevel.DataContext.GetType().GetProperty(fullName, BindingFlags.Public | BindingFlags.Instance);
if (prop != null && typeof(ICommand).IsAssignableFrom(prop.PropertyType))
{
return (ICommand?)prop.GetValue(topLevel.DataContext);
}
}
// 2. 当前控件:如果是 ICommandSource 并且匹配名称
if (topLevel is ICommandSource commandSource && commandSource.Command != null)
{
// 通过控件 Name 或 Command 类型名匹配
if (topLevel.Name == name || topLevel.Name == fullName ||
commandSource.Command.GetType().Name == name || commandSource.Command.GetType().Name == fullName)
return commandSource.Command;
}
// 3. 递归子控件
foreach (var child in topLevel.GetVisualChildren())
{
if (child is Control c)
{
return GetICommand(c, name);
}
}
return null;
}
public static IDisposable SetICommand(this Control topLevel, ICommand command, KeyGesture? keyGesture = null, AvaloniaProperty? targetProperty = null)
{
var disposables = new CompositeDisposable();
// 1. 监听 targetProperty 的值变化,执行命令(先 CanExecute)
if (targetProperty != null)
{
var subscription = topLevel.GetObservable(targetProperty)
.Subscribe(value =>
{
if (command.CanExecute(value))
{
command.Execute(value);
}
});
disposables.Add(subscription);
}
// 2. 绑定命令
if (topLevel is ICommandSource commandSource)
{
commandSource.SetPropertyValue("Command", command);
}
else
// 3. 否则,尝试添加 KeyBinding
{
var keyBinding = new KeyBinding
{
Command = command,
Gesture = keyGesture!
};
bool exists = topLevel.KeyBindings.Any(kb => kb.Command == command && kb.Gesture == keyGesture);
if (!exists)
{
topLevel.KeyBindings.Add(keyBinding);
}
else
{
Console.WriteLine("SetICommand:命令已存在于控件的 KeyBindings 中,未重复添加。");
}
}
return disposables;
}
//Rect转Point[]
public static Point[] RectToPoints(this Rect rect)
{
return new Point[]
{
new Point(rect.Left, rect.Top), // 左上
new Point(rect.Right, rect.Top), // 右上
new Point(rect.Right, rect.Bottom), // 右下
new Point(rect.Left, rect.Bottom) // 左下
};
}
//动态修改MiterLimit
public static void SetMiterLimit(this Shape shape, double limit)
{
var oldPen = shape.GetFieldValue<IPen>("_strokePen");
// 创建新的 ImmutablePen
if (oldPen != null)
{
var newPen = new ImmutablePen(
oldPen.Brush as IImmutableBrush,
oldPen.Thickness,
oldPen.DashStyle as ImmutableDashStyle,
oldPen.LineCap,
oldPen.LineJoin,
limit
);
// 替换 _strokePen
shape.SetFieldValue("_strokePen", newPen);
shape.InvalidateVisual(); // 刷新显示
}
}
//动态加载资源或者样式
public static void Load(this Control topLevel, string path)
{
var uri = new Uri(path, UriKind.RelativeOrAbsolute);
// 尝试作为 ResourceInclude 加入 Resources.MergedDictionaries
bool alreadyResource = topLevel.Resources.MergedDictionaries
.OfType<ResourceInclude>()
.Any(r => r.Source == uri);
if (!alreadyResource)
{
var include = new ResourceInclude(uri)
{
Source = uri
};
try
{
topLevel.Resources.MergedDictionaries.Add(include);
}
catch
{
// 不是 ResourceDictionary,尝试作为 Styles
bool alreadyStyle = topLevel.Styles
.OfType<StyleInclude>()
.Any(s => s.Source == uri);
if (!alreadyStyle)
{
var styleInclude = new StyleInclude(uri)
{
Source = uri
};
topLevel.Styles.Add(styleInclude);
}
}
}
}
public static void LayoutTransform(this Control topLevel)
{
if (topLevel == null)
{
Console.WriteLine($"{nameof(topLevel)}不能为空");
return;
}
// 找到父容器
var parent = topLevel.Parent as Panel;
if (parent == null)
return;
// 找到控件在父容器中的位置
var index = parent.Children.IndexOf(topLevel);
if (index < 0)
return;
// 从父容器移除原控件
parent.Children.RemoveAt(index);
parent.Children.Insert(index, new LayoutTransformControl
{
Child = topLevel
});
}
}
}
Reflection.cs代码
using System;
using System.Linq;
using System.Reflection;
namespace Shares
{
public static class Reflection
{
public static T? GetPropertyValue<T>(this object obj, string propertyName)
{
if (obj == null)
{
Console.WriteLine("GetPropertyValue:传入对象为 null");
return default;
}
Type? type = obj.GetType();
PropertyInfo? prop = null;
while (type != null)
{
prop = type.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (prop != null)
break;
type = type.BaseType;
}
if (prop == null)
{
Console.WriteLine($"未找到属性 '{propertyName}',类型链中没有该属性");
return default;
}
var value = prop.GetValue(obj);
if (value is T t)
return t;
try
{
return (T?)Convert.ChangeType(value, typeof(T));
}
catch
{
Console.WriteLine($"属性 '{propertyName}' 类型不匹配。期望 {typeof(T).FullName},实际 {value?.GetType().FullName ?? "null"}");
return default;
}
}
public static bool SetPropertyValue<T>(this object obj, string propertyName, T value)
{
if (obj == null)
{
Console.WriteLine("SetPropertyValue:目标对象为 null");
return false;
}
if (string.IsNullOrEmpty(propertyName))
{
Console.WriteLine("SetPropertyValue:属性名为空");
return false;
}
Type? type = obj.GetType();
PropertyInfo? property = null;
while (type != null)
{
property = type.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null)
break;
type = type.BaseType;
}
if (property == null)
{
Console.WriteLine($"SetPropertyValue:未找到属性 '{propertyName}',类型链中没有该属性");
return false;
}
if (!property.CanWrite)
{
Console.WriteLine($"SetPropertyValue:属性 '{propertyName}' 不可写");
return false;
}
try
{
property.SetValue(obj, value);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"SetPropertyValue:设置属性 '{propertyName}' 失败,异常:{ex}");
return false;
}
}
public static T? GetFieldValue<T>(this object obj, string fieldName)
{
if (obj == null)
{
Console.WriteLine("GetFieldValue:传入对象为 null");
return default;
}
Type? type = obj.GetType();
FieldInfo? field = null;
while (type != null)
{
field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
if (field != null)
break;
type = type.BaseType;
}
if (field == null)
{
Console.WriteLine($"未找到字段 '{fieldName}',类型链中没有该字段");
return default;
}
var value = field.GetValue(obj);
if (value is T t)
return t;
try
{
return (T?)Convert.ChangeType(value, typeof(T));
}
catch
{
Console.WriteLine($"字段 '{fieldName}' 类型不匹配。期望 {typeof(T).FullName},实际 {value?.GetType().FullName ?? "null"}");
return default;
}
}
public static bool SetFieldValue<T>(this object obj, string fieldName, T value)
{
if (obj == null)
{
Console.WriteLine("SetFieldValue:目标对象为 null");
return false;
}
if (string.IsNullOrEmpty(fieldName))
{
Console.WriteLine("SetFieldValue:字段名为空");
return false;
}
Type? type = obj.GetType();
FieldInfo? field = null;
while (type != null)
{
field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
if (field != null)
break;
type = type.BaseType;
}
if (field == null)
{
Console.WriteLine($"SetFieldValue:未找到字段 '{fieldName}',类型链中没有该字段");
return false;
}
try
{
field.SetValue(obj, value);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"SetFieldValue:设置字段 '{fieldName}' 失败,异常:{ex}");
return false;
}
}
public static object? InvokeMethod(this object obj, string methodName, params object?[]? parameters)
{
if (obj == null)
{
Console.WriteLine("InvokeMethod:目标对象为 null");
return null;
}
if (string.IsNullOrWhiteSpace(methodName))
{
Console.WriteLine("InvokeMethod:方法名为空");
return null;
}
parameters ??= Array.Empty<object?>();
Type? type = obj.GetType();
MethodInfo? method = null;
while (type != null)
{
var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
.Where(m => m.Name == methodName);
foreach (var m in methods)
{
var ps = m.GetParameters();
if (ps.Length == parameters.Length)
{
method = m;
break;
}
}
if (method != null)
break;
type = type.BaseType;
}
if (method == null)
{
Console.WriteLine($"InvokeMethod:未找到方法 '{methodName}',参数个数:{parameters.Length}");
return null;
}
try
{
var result = method.Invoke(obj, parameters);
return method.ReturnType == typeof(void) ? null : result;
}
catch (Exception ex)
{
Console.WriteLine($"InvokeMethod:调用 '{methodName}' 异常:{ex.Message}");
return null;
}
}
public static T? InvokeMethod<T>(this object obj, string methodName, params object?[]? parameters)
{
var result = obj.InvokeMethod(methodName, parameters);
return result is T t ? t : default;
}
}
}
CompilerService.cs代码
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;
namespace Shares.Avalonia
{
public static class CompilerService
{
public static bool IsBrowser()
{
return RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"));
}
private static PortableExecutableReference[]? s_references;
public static string? BaseUri { get; set; }
private static async Task LoadReferences()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
if (IsBrowser())
{
if (BaseUri is null)
{
return;
}
var appDomainReferences = new List<PortableExecutableReference>();
var client = new HttpClient
{
BaseAddress = new Uri(BaseUri)
};
Console.WriteLine($"Loading references BaseUri: {BaseUri}");
foreach (var reference in assemblies.Where(x => !x.IsDynamic))
{
try
{
var name = reference.GetName().Name;
var requestUri = $"{BaseUri}managed/{name}.dll";
Console.WriteLine($"Loading reference requestUri: {requestUri}, FullName: {reference.FullName}");
var stream = await client.GetStreamAsync(requestUri);
appDomainReferences.Add(MetadataReference.CreateFromStream(stream));
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
s_references = appDomainReferences.ToArray();
}
else
{
var appDomainReferences = new List<PortableExecutableReference>();
foreach (var reference in assemblies.Where(x => !x.IsDynamic && !string.IsNullOrWhiteSpace(x.Location)))
{
appDomainReferences.Add(MetadataReference.CreateFromFile(reference.Location));
}
s_references = appDomainReferences.ToArray();
}
}
public static async Task<(Assembly? Assembly, AssemblyLoadContext? Context)> GetScriptAssembly(string code)
{
if (s_references is null)
{
await LoadReferences();
}
var stringText = SourceText.From(code, Encoding.UTF8);
var parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest);
var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(stringText, parseOptions);
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithOptimizationLevel(OptimizationLevel.Release);
var compilation = CSharpCompilation.Create(Path.GetRandomFileName(), new[] { parsedSyntaxTree }, s_references, compilationOptions);
using var ms = new MemoryStream();
var result = compilation.Emit(ms);
var errors = result.Diagnostics.Where(x => x.Severity == DiagnosticSeverity.Error);
if (!result.Success)
{
foreach (var error in errors)
{
Console.WriteLine(error);
}
return (null, null);
}
ms.Seek(0, SeekOrigin.Begin);
var context = new AssemblyLoadContext(name: Path.GetRandomFileName(), isCollectible: true);
var assembly = context.LoadFromStream(ms);
return (assembly, context);
}
public static async Task<(Assembly? Assembly, AssemblyLoadContext? Context)> GetScriptAssembly(Dictionary<string, string> sourceFiles)
{
if (s_references is null)
await LoadReferences();
var syntaxTrees = new List<SyntaxTree>();
var parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest);
foreach (var kv in sourceFiles)
{
var sourceText = SourceText.From(kv.Value, Encoding.UTF8);
var syntaxTree = SyntaxFactory.ParseSyntaxTree(sourceText, parseOptions, path: kv.Key);
syntaxTrees.Add(syntaxTree);
}
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithOptimizationLevel(OptimizationLevel.Release);
var compilation = CSharpCompilation.Create(
assemblyName: Path.GetRandomFileName(),
syntaxTrees: syntaxTrees,
references: s_references,
options: compilationOptions
);
using var ms = new MemoryStream();
var result = compilation.Emit(ms);
if (!result.Success)
{
foreach (var diag in result.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error))
Console.WriteLine(diag);
return (null, null);
}
ms.Seek(0, SeekOrigin.Begin);
var context = new AssemblyLoadContext(Path.GetRandomFileName(), isCollectible: true);
var assembly = context.LoadFromStream(ms);
return (assembly, context);
}
}
}
浙公网安备 33010602011771号