Вы никогда не сможете получить полностью общую реализацию оператора ?
. Оператор может быть реализован по-разному для различных типов, где может потребоваться сделать что-то особенное в зависимости от типа:
- Для
Dictionary<T, R>
вы хотите использовать функцию поиска в словаре
- Для объектов SQL в моей статье, на которую вы ссылались, вы хотите, чтобы она использовала определенный API SQL
- Для неизвестных объектов .NET вы хотите, чтобы он использовал .NET Reflection
Если вы ищете реализацию, которая использует Reflection, то вы можете использовать ту, которую я реализовал в F # привязке для MonoDevelop ( доступно на GitHub ). Он достаточно полон и обрабатывает доступ к свойствам, вызовы методов, а также статические члены. (Остальная часть связанного файла использует его для вызова внутренних членов компилятора F #). Он напрямую использует Reflection, поэтому он довольно медленный, но вполне функциональный.
Другой альтернативой может быть реализация оператора поверх .NET 4.0 Dynamic Language Runtime (чтобы он использовал тот же базовый API, что и dynamic
в C # 4). Я не думаю, что где-то есть реализация этого, но вот простой пример того, как вы можете получить это:
#r "Microsoft.CSharp.dll"
open System
open System.Runtime.CompilerServices
open Microsoft.CSharp.RuntimeBinder
let (?) (inst:obj) name (arg:'T) : 'R =
// Create site (representing dynamic operation for converting result to 'R
let convertSite =
CallSite<Func<CallSite, Object, 'R>>.Create //'
(Binder.Convert(CSharpBinderFlags.None, typeof<'R>, null)) //'
// Create site for the method call with single argument of type 'T
let callSite =
CallSite<Func<CallSite, Object, 'T, Object>>.Create //'
(Binder.InvokeMember
( CSharpBinderFlags.None, name, null, null,
[| CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null);
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) |]))
// Run the method and perform conversion
convertSite.Target.Invoke
(convertSite, callSite.Target.Invoke(callSite, inst, arg))
let o = box (new Random())
let a : int = o?Next(10)
Это работает только для вызовов методов экземпляра с одним аргументом (Вы можете узнать, как это сделать, посмотрев код, сгенерированный компилятором C # для вызовов dynamic
). Я предполагаю, что если вы смешаете полноту (из первого) с подходом к использованию DLR (во втором), вы получите самую надежную реализацию, какую только сможете получить.
РЕДАКТИРОВАТЬ: Я также отправил код в F # Snippets. Вот версия с использованием DLR: http://fssnip.net/2U, а вот версия из плагина F # (с использованием .NET Reflection): http://fssnip.net/2V