从JSON文件中解析c#函数
本文关键字:函数 JSON 文件 | 更新日期: 2023-09-27 18:07:23
我正在摆弄游戏道具执行,为了避免硬编码道具,我想从外部文件加载它们的信息和执行。
基本格式是这样的:
-- Items.json
[
{
"name": "Rusty Key",
"shortDesc": "A rusty key.",
"longDesc": "A slightly rusty key. You don't know what door it opens.",
"code": "RustyKey.cs"
}
]
或者直接将Use()
代码嵌入JSON文件:
-- Items.json
[
{
"name": "Rusty Key"
...
"use": "return 0;"
}
]
JSON文件将在运行时加载,并动态添加项目及其实现。
这也可能演变成一种简单的脚本语言,允许玩家创建自己的道具,但在目前的形式下,我不会向公众发布它,因为有可能被滥用。
所以我的问题是:我如何加载/解析附加的脚本文件或嵌入代码,并将其附加到动态创建的对象?
创建MyLibrary.dll
并添加以下代码:
namespace MyTypesNamespace
{
public abstract class BaseItem
{
public abstract bool Use(object onMyObject);
}
public class Door
{
public bool IsLocked { get; set; }
public bool Open()
{
if (IsLocked)
{
System.Console.WriteLine("Cannot open door. It is locked!");
return false;
}
//Some code
System.Console.WriteLine("Door is opened!");
return true;
}
}
}
在主项目中,添加对MyLibrary.dll
的引用和以下方法:
private static List<BaseItem> loadItems(string fromCode)
{
CodeDomProvider codeProvider = new CSharpCodeProvider();
// add compiler parameters
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.CompilerOptions = "/target:library /optimize";
compilerParams.GenerateExecutable = false;
compilerParams.GenerateInMemory = true;
compilerParams.IncludeDebugInformation = false;
compilerParams.ReferencedAssemblies.Add("mscorlib.dll");
compilerParams.ReferencedAssemblies.Add("System.dll");
compilerParams.ReferencedAssemblies.Add("MyLibrary.dll");
// compile the code
CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams, fromCode);
var items = new List<BaseItem>();
foreach (var itemType in results.CompiledAssembly.DefinedTypes)
{
ConstructorInfo ctor = itemType.GetConstructor(Type.EmptyTypes);
object instance = ctor.Invoke(null);
items.Add(instance as BaseItem);
}
return items;
}
用法:
private static void Main()
{
string code = loadCode();
List<BaseItem> items = loadItems(code);
BaseItem rustyKey = items[0];
BaseItem unlockAnyDoor = items[1];
Door myDoor = new Door { IsLocked = true };
rustyKey.Use(myDoor);
unlockAnyDoor.Use(myDoor);
rustyKey.Use(myDoor);
Console.ReadLine();
}
private static string loadCode()
{
return @"
using MyTypesNamespace;
public class RustyKey : BaseItem
{
public override bool Use(object onMyObject)
{
var door = onMyObject as Door;
return door.Open();
}
}
public class UnlockAnyDoor : BaseItem
{
public override bool Use(object onMyObject)
{
var door = onMyObject as Door;
door.IsLocked = false;
System.Console.WriteLine(""Door is unlocked!"");
return true;
}
}
";
}
输出:(见在线输出)
无法打开门。门锁上了!门没锁!门开了!之前
编辑:您可以通过消除代码重复来简化将来的代码提供(将从文件中读取)。让
generateItemClass
方法取className
和Use()
方法体:private static string generateItemClass(string className, string useBody) { return $@" public class {className} : BaseItem {{ public override bool Use(object onMyObject) {{ {useBody} }} }}"; }
不要忘记为所有类添加
using MyTypesNamespace;
。private static string loadCode() { string namespaces = @"using MyTypesNamespace;"; string rustyKeyClass = generateItemClass("RustyKey", @"var door = onMyObject as Door; return door.Open();"); string unlockAnyDoorClass = generateItemClass("UnlockAnyDoor", @"var door = onMyObject as Door; door.IsLocked = false; System.Console.WriteLine(""Door is unlocked!""); return true;"); return namespaces + rustyKeyClass + unlockAnyDoorClass; }
将原始代码放入JSON意味着必须拾取该代码,可能对其进行解析,将其粘合到某些代码外壳中,编译它并检查错误。那是很多机器。(另一个答案提供了这样做的细节)。
还有一个问题,如果有人可以在那里编写任意代码,他们就会这样做,并且他们会以任意奇怪的方式编写一些东西来破坏你的应用程序,要么造成安全漏洞,要么造成一些困难的调试问题。
我认为在大多数情况下,你最好只是在JSON中标记你想要的内容,例如,
-- Items.json
[
{
"name": "Rusty Key"
...
"result": "0"
}
]
现在程序逻辑读取JSON文件,存储<"RustyKey","0">放入散列查找集合中,然后当出现对RustyKey的请求时,在集合中查找结果值。
当然,你不能像include代码那样做那么多复杂的事情,但是你会遇到更少的麻烦。
格言:"eval is evil"