我正在搞乱游戏项目的实现,为了避免对项目进行硬编码,我想从外部文件中加载他们的信息和实现。
基本格式如下:
-- 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文件将在运行时加载并动态添加项目及其实现。
这也可能变成一种简单的脚本语言,允许玩家创建自己的项目,但是由于可能存在滥用行为,我现在不会将其发布给公众。
所以我的问题是:如何加载/解析附加的脚本文件或嵌入代码并将其附加到动态创建的对象?
答案 0 :(得分:0)
创建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;
}
}
";
}
Cannot open door. It is locked! Door is unlocked! Door is opened!
您可以通过消除代码重复来简化将来的代码供应(将从文件中读取)。设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;
}
答案 1 :(得分:0)
将原始代码放入JSON意味着必须获取该代码,可能会解析它,将其粘贴到某些代码shell中,编译它并检查错误。这是很多机器。 (另一个答案非常详细地说明了这一点)。
还有一个问题是,如果有人可以在那里编写任意代码,他们会,并且他们会写出一些以任意奇怪的方式破坏你的应用程序的东西,要么是造成安全漏洞,要么是没有其他一些困难的调试问题。
我认为在大多数情况下你会更好地在JSON中发出你想要的信号,例如,
-- Items.json
[
{
"name": "Rusty Key"
...
"result": "0"
}
]
现在程序逻辑读取JSON文件,存储&lt;“RustyKey”,“0”&gt;进入哈希查找集合,然后当出现RustyKey请求时,查找集合中的结果值。
当然,你不能像包含代码一样做很复杂的事情,但是你会遇到很多麻烦。
座右铭:“eval是邪恶的”。