конструктор как делегат-возможно ли это в C#?
у меня есть класс, как показано ниже:
class Foo
{
public Foo(int x) { ... }
}
и мне нужно передать в определенный метод делегат вроде этого:
delegate Foo FooGenerator(int x);
можно ли передать конструктор непосредственно как FooGenerator
значение, без необходимости вводить:
delegate(int x) { return new Foo(x); }
?
EDIT: для моего личного использования вопрос относится к .NET 2.0, но подсказки/ответы для 3.0+ также приветствуются.
7 ответов:
нет, среда CLR не позволяет привязывать делегаты к
вы можете просто создать свой собственный:ConstructorInfo
.static T Make<T>(Action<T> init) where T : new() { var t = new T(); init(t); return t; }
использование
var t = Make<Foo>( x => { x.Bar = "bar"; x.Baz = 1; });
Я предполагаю, что вы обычно делаете что-то подобное в рамках реализации фабрики, где фактические типы не известны во время компиляции...
во-первых, обратите внимание, что более простой подход может быть шагом инициализации после создания, тогда вы можете использовать дженерики:
static T Create<T>({args}) where T : class, ISomeInitInterface, new() { T t = new T(); t.Init(args); return t; }
вы можете использовать
MakeGenericMethod
и/илиCreateDelegate
.
в противном случае; вы можете сделать это на лету с
Expression
(3.5) илиDynamicMethod
(2.0).The
Expression
подход проще кодировать:var param = Expression.Parameter(typeof(int), "val"); var ctor = typeof(Foo).GetConstructor(new[] { typeof(int) }); var lambda = Expression.Lambda<Func<int, Foo>>( Expression.New(ctor, param), param); var func = lambda.Compile(); Foo foo = func(123); string s = foo.ToString(); // proof
или (используя
DynamicMethod
):ConstructorInfo ctor = typeof(Foo).GetConstructor(new[] { typeof(int) }); DynamicMethod dm = new DynamicMethod("Create", typeof(Foo), new Type[] { typeof(int) }, typeof(Foo), true); ILGenerator il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Newobj, ctor); il.Emit(OpCodes.Ret); Converter<int, Foo> func = (Converter<int, Foo>) dm.CreateDelegate(typeof(Converter<int, Foo>)); Foo foo = func(123); string s = foo.ToString(); // proof
Я думаю, что так же лаконично, как вы собираетесь получить (не переходя к заводскому шаблону) , будет что-то с анонимными методами, например:
delegate Foo FooGenerator(int x); ... void DoStuff() { YourDelegateConsumer(x => new Foo(x)); }
Это не делает строго то, что вы просили (так как вы передаете делегат анонимному методу, который возвращает новый экземпляр, а не прямой делегат конструктору), но я не думаю, что то, что вы просите, строго возможно.
это, конечно, при условии, что вы используете 3.5+
к сожалению, нет, конструкторы не совсем то же самое, что методы, и поэтому вы не можете создать делегат, который указывает на них. Это интересная идея, хотя, возможно, с большей информацией мы могли бы разработать какой-то обходной путь, который был бы синтаксически похож.
Марк Gravell вдохновил меня на следующее Очень простое решение:
static void Main() { Pet a = _MakeObject(typeof(Dog)); Pet b = _MakeObject(typeof(Cat)); } private static Pet _MakeObject(Type type) { ConstructorInfo info = type.GetConstructor(new Type[0]); return (Pet)info?.Invoke(null); }
почти то же самое, если конструктор имеет параметры (в данном примере: 1 параметр типа int):
static void Main() { Pet a = _MakeObject(typeof(Dog), 5); Pet b = _MakeObject(typeof(Cat), 7); } private static Pet _MakeObject(Type type, int age) { ConstructorInfo info = type.GetConstructor(new [] { typeof(int) }); return (Pet)info?.Invoke(new object[] { age }); }