222 lines
7.6 KiB
C#
222 lines
7.6 KiB
C#
using System;
|
|
using System.CodeDom.Compiler;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using BITKit.Mod;
|
|
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
|
using Microsoft.CodeAnalysis.Emit;
|
|
using Microsoft.CodeAnalysis.Scripting;
|
|
#if UNITY_5_3_OR_NEWER
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
using static BITKit.Mod.DotNetSdkRoslynService;
|
|
#endif
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace BITKit
|
|
{
|
|
public class BITSharp
|
|
{
|
|
#if UNITY_5_3_OR_NEWER
|
|
[RuntimeInitializeOnLoadMethod]
|
|
private static void Reload()
|
|
{
|
|
ReferencedAssemblies.Clear();
|
|
Dictionary.Clear();
|
|
assemblies = null;
|
|
|
|
}
|
|
#endif
|
|
public static readonly List<string> ReferencedAssemblies = new();
|
|
private static readonly ConcurrentDictionary<string, Type> Dictionary = new();
|
|
private static Assembly[] assemblies;
|
|
|
|
public static bool TryGetTypeFromFullName(string fullClassName, out Type type)
|
|
{
|
|
type = GetTypeFromFullName(fullClassName);
|
|
return type is not null ? true : false;
|
|
}
|
|
|
|
public static Type GetTypeFromFullName(string fullClassName)
|
|
{
|
|
return Dictionary.GetOrAdd(fullClassName, SearchType);
|
|
}
|
|
|
|
private static Type SearchType(string fullName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(fullName))
|
|
{
|
|
throw new ArgumentException("fullName is null or empty", nameof(fullName));
|
|
}
|
|
assemblies ??= AppDomain.CurrentDomain.GetAssemblies();
|
|
return assemblies.Select(assembly => assembly.GetType(fullName, false)).FirstOrDefault(type => type is not null);
|
|
}
|
|
|
|
public static IReadOnlyCollection<Assembly> GetReferencedAssemblies(Type type)
|
|
{
|
|
var result = new HashSet<Assembly>();
|
|
foreach (var fieldInfo in type.GetFields())
|
|
{
|
|
result.Add(fieldInfo.FieldType.Assembly);
|
|
}
|
|
|
|
foreach (var propertyInfo in type.GetProperties())
|
|
{
|
|
result.Add(propertyInfo.PropertyType.Assembly);
|
|
}
|
|
foreach (var methodInfo in type.GetMethods())
|
|
{
|
|
result.Add(methodInfo.ReturnType.Assembly);
|
|
foreach (var argument in methodInfo.GetGenericArguments())
|
|
{
|
|
result.Add(argument.Assembly);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static string GenerateCodeFromInterface(Type type,string namespaceName = null)
|
|
{
|
|
if (type.IsInterface is false) throw new InvalidDataException("The type have to be interface");
|
|
|
|
var codeBuilder = new StringBuilder();
|
|
|
|
var nameSpaces = new HashSet<string>();
|
|
|
|
HashSet<Type> allTypes = new();
|
|
HashSet<MethodInfo> propertyMethods = new();
|
|
|
|
foreach (var propertyInfo in type.GetProperties())
|
|
{
|
|
if (propertyInfo.GetMethod is not null)
|
|
{
|
|
propertyMethods.Add(propertyInfo.GetMethod);
|
|
}
|
|
|
|
if (propertyInfo.SetMethod is not null)
|
|
{
|
|
propertyMethods.Add(propertyInfo.SetMethod);
|
|
}
|
|
}
|
|
|
|
foreach (var method in type.GetMethods())
|
|
{
|
|
if (method.DeclaringType is not null)
|
|
{
|
|
nameSpaces.Add(method.DeclaringType.Namespace);
|
|
}
|
|
nameSpaces.Add(method.ReturnType.Namespace);
|
|
foreach (var argument in method.GetGenericArguments())
|
|
{
|
|
nameSpaces.Add(argument.Namespace);
|
|
}
|
|
}
|
|
|
|
nameSpaces.Add("System");
|
|
nameSpaces.Add("System.Collections.Concurrent");
|
|
nameSpaces.Add("System.Collections.Generic");
|
|
|
|
|
|
|
|
codeBuilder.AppendLine(string.Join('\n', nameSpaces.Select(x=>$"using {x};")));
|
|
|
|
|
|
//codeBuilder.AppendLine("namespace BITSharpGen");
|
|
//codeBuilder.AppendLine("{");
|
|
|
|
codeBuilder.AppendLine($"public class @{type.Name}Gen : {type.Name}");
|
|
codeBuilder.AppendLine("{");
|
|
|
|
|
|
foreach (var propertyInfo in type.GetProperties())
|
|
{
|
|
codeBuilder.AppendLine($"public {CSharpName(propertyInfo.PropertyType)} {propertyInfo.Name}" + "{ get;set; }");
|
|
}
|
|
|
|
// 遍历接口的所有成员,生成方法的默认实现
|
|
foreach (var method in type.GetMethods())
|
|
{
|
|
if(propertyMethods.Contains(method))continue;
|
|
string methodName = method.Name;
|
|
string parameters = string.Join(", ", method.GetParameters()
|
|
.Select(p => $"{CSharpName(p.ParameterType)} {p.Name}"));
|
|
|
|
// 检查是否为泛型方法
|
|
if (method.IsGenericMethod)
|
|
{
|
|
// 处理泛型方法
|
|
var genericArgs = string.Join(", ", method.GetGenericArguments()
|
|
.Select(arg => CSharpName(arg)));
|
|
|
|
if (method.ReturnType == typeof(void))
|
|
{
|
|
codeBuilder.AppendLine($" public void {methodName}<{genericArgs}>({parameters}) {{ }}");
|
|
}
|
|
else
|
|
{
|
|
codeBuilder.AppendLine($" public {CSharpName(method.ReturnType)} {methodName}<{genericArgs}>({parameters}) {{ return default({CSharpName(method.ReturnType)}); }}");
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (method.ReturnType == typeof(void))
|
|
{
|
|
codeBuilder.AppendLine($" public void {methodName}({parameters}) {{ }}"); // Void 方法默认空实现
|
|
}
|
|
else
|
|
{
|
|
// 返回默认值
|
|
codeBuilder.AppendLine($" public {CSharpName(method.ReturnType)} {methodName}({parameters}) {{ return default({CSharpName(method.ReturnType)}); }}");
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//codeBuilder.AppendLine("\t}");
|
|
|
|
codeBuilder.AppendLine("}");
|
|
|
|
return codeBuilder.ToString();
|
|
}
|
|
|
|
public static string CSharpName(Type type)
|
|
{
|
|
var sb = new StringBuilder();
|
|
var name = type.Name;
|
|
if (!type.IsGenericType) return name;
|
|
sb.Append(name.Substring(0, name.IndexOf('`')));
|
|
sb.Append("<");
|
|
sb.Append(string.Join(", ", type.GetGenericArguments()
|
|
.Select(t => CSharpName(t))));
|
|
sb.Append(">");
|
|
return sb.ToString();
|
|
}
|
|
|
|
public static Assembly Compile(string code,ScriptOptions scriptOptions=null)
|
|
{
|
|
scriptOptions ??= ScriptOptions.Default;
|
|
|
|
var script = CSharpScript.Create(code,scriptOptions);
|
|
script.Compile();
|
|
|
|
using var ms = new MemoryStream();
|
|
var result = script.GetCompilation().Emit(ms);
|
|
ms.Seek(0, SeekOrigin.Begin);
|
|
var assembly = Assembly.Load(ms.ToArray()); // 加载程序集
|
|
|
|
return assembly;
|
|
}
|
|
}
|
|
} |