Mono.Cecil を使用して、次のような型を挿入しました。
internal class InjectedDelegateCommand<TParameter> : ICommand {
// Fields
private readonly Predicate<TParameter> _canExecute;
private readonly Action<TParameter> _execute;
// Events
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
// Methods
public InjectedDelegateCommand(Action<TParameter> execute) : this(execute, null){}
public InjectedDelegateCommand(Action<TParameter> execute, Predicate<TParameter> canExecute) {
if (execute == null) {
throw new ArgumentNullException("execute");
}
this._execute = execute;
this._canExecute = canExecute;
}
public bool CanExecute(object parameter) {
return ((this._canExecute == null) || this._canExecute((TParameter) parameter));
}
public void Execute(object parameter) {
this._execute((TParameter) parameter);
}
}
次のコマンド タイプでクラスのプロパティを初期化するコードを挿入する必要があります。
public class ViewModel {
public ViewModel(){
SubmitCommand = new InjectedDelegateCommand<string>(
new Action<string>(OnSubmit)
, new Predicate<string>(CanSubmit));
}
public ICommand SubmitCommand {get; set;}
public void OnSubmit(string parameter){}
public bool CanSubmit(string parameter) {
return true;
}
}
残念ながら、cecil でコンストラクター呼び出しを適切に実装する方法について、頭を悩ませているようには思えません。
誰でも助けることができますか?どの IL を生成する必要があるかには興味がありませんが、コンストラクターがこれらの行を実装するための適切な MethodReference を取得する方法には興味がありません。
SubmitCommand = new InjectedDelegateCommand<string>(
new Action<string>(OnSubmit)
, new Predicate<string>(CanSubmit));
編集- 私が試したことを含みます。
使用したいコンストラクターを取得するために以下のメソッドを作成しようとしましたが、ジェネリックが間違っています:
private MethodReference GetConstructorResolved(MethodReference commandConstructor, MethodDefinition method)
{
var parameterType = method.HasParameters
? method.Parameters[0].ParameterType
: Assets.TypeReferences.Object;
var commandType = commandConstructor.DeclaringType;
bool isGeneric = commandType.HasGenericParameters;
if (commandType.HasGenericParameters)
{
var commandTypeResolved = commandConstructor.DeclaringType.MakeGenericInstanceType(parameterType);
commandType = commandTypeResolved;
var resolvedConstructor =
commandType.GetElementType()
.Resolve()
.GetConstructors()
.First(c => c.Parameters.Count == commandConstructor.Parameters.Count);
commandConstructor = resolvedConstructor;
}
if (isGeneric)
{
if (method.HasParameters)
{
commandConstructor =
commandConstructor.MakeHostInstanceGeneric(method.Parameters[0].ParameterType);
}
else
{
commandConstructor = commandConstructor.MakeHostInstanceGeneric(Assets.TypeReferences.Object);
}
}
return commandConstructor;
}
MakeHostGeneric の由来: https://stackoverflow.com/a/16433452/203499
最終的に次のような IL になります (この例では、1 つの引数のコンストラクターを呼び出そうとしていることに注意してください。
IL_0013: ldftn instance void WpfMvvmSample.ViewModel::Submit(string)
IL_0019: newobj instance void class [mscorlib]System.Action`1<string>::.ctor(object, native int)
IL_001e: newobj instance void class 'InjectedDelegateCommand'<string>::.ctor(class [mscorlib]System.Action`1<!0>)
IL_0023: call instance void WpfMvvmSample.ViewModel::set_SubmitCommand(class [PresentationCore]System.Windows.Input.ICommand)