У меня есть следующий фрагмент кода:
string actionString = "(p1,p2)=>p1.HP-=20";
var options = ScriptOptions.Default.AddReferences(typeof(SimplePlayer).Assembly).AddImports("Player");
var script = CSharpScript.Create<Action<SimplePlayer, SimplePlayer>>(actionString, options);
SimpleDeck d = new SimpleDeck(Game.GameController.Cards, 100, 100);
SimplePlayer p1 = new SimplePlayer(4000, 100, 100, d);
SimplePlayer p2 = new SimplePlayer(4000, 100, 100, d);
var del = script.CreateDelegate();
del.DynamicInvoke(p1, p2);
System.Console.WriteLine(p1.HP);
Я хочу добиться того же, но вместо того, чтобы указывать тип ожидаемого делегата Я хочу что-то вроде этого:
string actionString = "(p1,p2)=>p1.HP-=20";
var options = ScriptOptions.Default.AddReferences(typeof(SimplePlayer).Assembly).AddImports("Player");
var script = CSharpScript.Create(actionString, options);
SimpleDeck d = new SimpleDeck(Game.GameController.Cards, 100, 100);
SimplePlayer p1 = new SimplePlayer(4000, 100, 100, d);
SimplePlayer p2 = new SimplePlayer(4000, 100, 100, d);
var del = script.CreateDelegate();
del.DynamicInvoke(p1, p2);
Меня бросает:
ошибка CS8917: невозможно определить тип делегата.
потому что я не указываю тип, который я хочу создать. Если я изменю код на:
string actionString = "(SimplePlayer p1,SimplePlayer p2)=>p1.HP-=20";
Есть ли способ вывести тип SimplePlayer без явной передачи его в качестве аргумента? И не нужно делать следующее:
var script = CSharpScript.Create<Action<SimplePlayer, SimplePlayer>>(actionString, options);
Если вам нужно, чтобы делегаты разрешались во время выполнения (другими словами, без дженериков), одним неуклюжим, но рабочим способом будет процедурная интерполяция в соответствующий тип Action
вокруг выражения перед передачей его в CSharpScript
. например
string code = Typed(
"(p1, p2) => p1.HP - 20",
nameof(SimplePlayer), // first arg type name
nameof(SimplePlayer) // second arg type name
);
// ((Action<SimplePlayer, SimplePlayer>)((p1, p2) => p1.HP - 20))
static string Typed(string code, params string[] args)
=> $"((Action<{string.Concat(", ", args)}>)({code}))";
Если возвращаемое значение может существовать или не существовать, вы можете так же легко заменить строку.
Редактировать: чтобы использовать любой тип делегата, который вы хотите, и не ограничиваться перегрузками Action
, вы можете просто использовать Type.Name в качестве интерполяции. Имейте в виду, что типы с дженериками не будут работать в приведенном ниже примере. Для этого обратитесь к этому ответу.
static string Typed(string code, Type type)
=> $"(({type.Name})({code}))";
Хотя желательно, если у вас есть возможность использовать дженерики, вы можете просто сделать что-то вроде этого.
Run<TDelegate>(string actionString, ScriptOptions options)
where T : Delegate
{
var del = CSharpScript.Create<TDelegate>(actionString, options);
// del is now guaranteed to be 'Delegate' and you can treat it as such, and you can use typeof(T) to do whatever reflection APIs you require
}
На самом деле это не работает, потому что обходной путь должен заключаться в интерполяции типа, который я хочу вывести.
@RafaelAcosta Я забыл, что правки не уведомляют людей; Я отредактировал этот пост, чтобы отразить то, что вы упомянули.
Эмик очень помогает, спасибо
Я так и думал, но хотелось чего-то более элегантного. Я думаю, это единственный способ, который не делает вещи слишком сложными. Спасибо