Никто не предоставил « классическое решение Reflection », так что вот полный пример кода:
using System;
using System.Collections;
using System.Collections.Generic;
namespace DictionaryRuntime
{
public class DynamicDictionaryFactory
{
/// <summary>
/// Factory to create dynamically a generic Dictionary.
/// </summary>
public IDictionary CreateDynamicGenericInstance(Type keyType, Type valueType)
{
//Creating the Dictionary.
Type typeDict = typeof(Dictionary<,>);
//Creating KeyValue Type for Dictionary.
Type[] typeArgs = { keyType, valueType };
//Passing the Type and create Dictionary Type.
Type genericType = typeDict.MakeGenericType(typeArgs);
//Creating Instance for Dictionary<K,T>.
IDictionary d = Activator.CreateInstance(genericType) as IDictionary;
return d;
}
}
}
Выше DynamicDictionaryFactory
класс имеет метод
CreateDynamicGenericInstance(Type keyType, Type valueType)
, и он создает и возвращает экземпляр IDictionary, типы ключей и значений которых точно указаны в запросе keyType
и valueType
.
Вот полный пример того, как вызвать этот метод для создания экземпляра и использования Dictionary<String, int>
:
using System;
using System.Collections.Generic;
namespace DynamicDictionary
{
class Test
{
static void Main(string[] args)
{
var factory = new DictionaryRuntime.DynamicDictionaryFactory();
var dict = factory.CreateDynamicGenericInstance(typeof(String), typeof(int));
var typedDict = dict as Dictionary<String, int>;
if (typedDict != null)
{
Console.WriteLine("Dictionary<String, int>");
typedDict.Add("One", 1);
typedDict.Add("Two", 2);
typedDict.Add("Three", 3);
foreach(var kvp in typedDict)
{
Console.WriteLine("\"" + kvp.Key + "\": " + kvp.Value);
}
}
else
Console.WriteLine("null");
}
}
}
. Когда указанное консольное приложение выполняется, мы получаем правильный ожидаемый результат:
Dictionary<String, int>
"One": 1
"Two": 2
"Three": 3
Две опции для Вас:
Имеют функцию, принимают делегат ( Action
для обратного вызова, который ничего не возвращает, Func
для того, которое делает), и используйте анонимного делегата или Лямбда-выражение при вызове его.
Использование интерфейс
public static void DoWork(Action processAction)
{
// do work
if (processAction != null)
processAction();
}
public static void Main()
{
// using anonymous delegate
DoWork(delegate() { Console.WriteLine("Completed"); });
// using Lambda
DoWork(() => Console.WriteLine("Completed"));
}
, Если Вашему обратному вызову нужно было передать что-то ему, можно использовать параметр типа на Action
:
public static void DoWork(Action<string> processAction)
{
// do work
if (processAction != null)
processAction("this is the string");
}
public static void Main()
{
// using anonymous delegate
DoWork(delegate(string str) { Console.WriteLine(str); });
// using Lambda
DoWork((str) => Console.WriteLine(str));
}
, Если требуется несколько аргументов, можно добавить больше параметров типа к Action
. Если Вы нуждаетесь в типе возврата, как упомянутое использование Func
и заставляете возврат ввести последний параметр типа (Func<string, int>
, функция, принимающая строку и возвращающая интервал)
public interface IObjectWithX
{
void X();
}
public class MyObjectWithX : IObjectWithX
{
public void X()
{
// do something
}
}
public class ActionClass
{
public static void DoWork(IObjectWithX handlerObject)
{
// do work
handlerObject.X();
}
}
public static void Main()
{
var obj = new MyObjectWithX()
ActionClass.DoWork(obj);
}
Походит на идеальный рецепт для делегатов - в частности, обратные вызовы с делегатами состоят точно в том, как это обрабатывается в асинхронном шаблоне в.NET.
вызывающая сторона обычно передавала бы Вас некоторое состояние и делегат, и Вы храните их обоих в любом контексте, Вы имеете, затем звоните делегату, передающему его состояние и безотносительно результата, который Вы могли бы иметь.
Вы могли или сделать состояние всего object
или потенциально использовать универсального делегата и взять состояние соответствующего типа, например,
public delegate void Callback<T>(T state, OperationResult result)
Тогда:
public void DoSomeOperation(int otherParameterForWhateverReason,
Callback<T> callback, T state)
, Поскольку Вы используете.NET 3.5, Вы могли бы хотеть использовать существующее Func<...>
и Action<...>
типы делегата, но Вы можете находить, что она делает более ясным объявить Ваше собственное. (Имя может сделать это более ясным, для чего Вы используете его.)
Рассматриваемый объект должен будет реализовать интерфейс, обеспеченный Вами. Возьмите интерфейс в качестве параметра, и затем можно назвать любой метод, который представляет интерфейс. Иначе у Вас нет способа знать то, к чему объект способен. Это, или Вы могли взять делегата в качестве параметра и вызов это.
Существует ли причина не сделать, чтобы Ваша библиотека обеспечила общественное мероприятие, которое будет запущено, когда операция завершена? Тогда вызывающая сторона могла просто зарегистрироваться для обработки события, и Вы не должны волноваться о раздавании объектов или делегатов.
объект, реализовывая интерфейс, который Вы обеспечили, работал бы, но это, кажется, больше подход Java, чем подход.NET. События кажутся немного более чистыми мне.