Что-то типа

void RegisterAs(Func??? register)
{
    register<IService1, Service1>(); // <-- call func like this
    register<IService2, Service2>(); // <-- or this
    register<IService3, Service3>();
}

И я хочу назвать этот метод как

RegisterAs(services.AddScoped)

Или

RegisterAs(services.AddSingleton)

c#
0
iBener 4 Фев 2022 в 11:15
2
Сделать RegisterAs общим?
 – 
Llama
4 Фев 2022 в 11:20
Да. На самом деле все операторы LINQ принимают универсальные функции Funcs.
 – 
Panagiotis Kanavos
4 Фев 2022 в 11:20
Вероятно, связано: IServiceCollection расширения
 – 
Christian Gollhardt
4 Фев 2022 в 11:25
Имеют ли эти три интерфейса общий базовый интерфейс? Если это так, вы можете использовать общее ограничение void RegisterAs<T>(Func<T> f) where T: BaseInterface.
 – 
HimBromBeere
4 Фев 2022 в 11:33
1
Зачем Func<TValue, TResult>, если вы не используете возвращаемое значение? Вместо этого вы можете выбрать Action<T>.
 – 
HimBromBeere
4 Фев 2022 в 11:35

3 ответа

В C# нет такого типа, который позволил бы вам вызывать делегата с аргументами универсального типа, поэтому в общем случае это невозможно. Если бы в C# были типы более высокого типа, это было бы возможно, но я не думаю, что это произойдет в ближайшее время :(

Вместо двух параметров универсального типа вы можете разрешить вызывающей стороне передать Func<Type, Type, IServiceCollection>:

void RegisterAs(Func<Type, Type, IServiceCollection> register)
{
    register(typeof(IService1), typeof(Service1));
    register(typeof(IService2), typeof(Service2));
    register(typeof(IService3), typeof(Service3));
}

На callsite вы по-прежнему можете делать:

RegisterAs(services.AddScoped);

Поскольку это использует другая перегрузка AddScoped, которая занимает 2 Type с. а не 2 параметра универсального типа.

3
Sir Rufo 4 Фев 2022 в 11:40
Нам нужен Func<Type,Type,IServiceCollection> см. эту скрипку ;o)
 – 
Sir Rufo
4 Фев 2022 в 11:42
Хорошее решение (палец вверх). Я сделал то же самое, когда не смог найти решение.
 – 
iBener
4 Фев 2022 в 11:48

Что ж, можно передать Func<> с параметрами типа. Эти параметры нельзя использовать в качестве универсальных. Вы можете использовать typeof() и передать это как параметр.

Но если вы хотите иметь только переменное время жизни, вы можете сделать это двумя разными способами, в этом случае нет необходимости передавать Func<>. Просто передайте время жизни в качестве параметра (первые два примера):

var sc = new ServiceCollection();

// With generics types
void AddMe<T, T2>(ServiceLifetime lifetime)
{
    sc.Add(ServiceDescriptor.Describe(typeof(T), typeof(T2), lifetime));
}

// Or like you have
void RegisterAs(ServiceLifetime lifetime)
{
   sc.Add(ServiceDescriptor.Describe(typeof(IService1), typeof(Service1), lifetime));
   sc.Add(ServiceDescriptor.Describe(typeof(IService2), typeof(Service2), lifetime));
   sc.Add(ServiceDescriptor.Describe(typeof(IService3), typeof(Service3), lifetime));
}

// Via a passed Func - not my preferred solution
void RegisterAsWithFunc(Func<Type, Type, IServiceCollection> register)
{
    register(typeof(IService1), typeof(Service1));
}

Звонок будет таким же простым, как

RegisterAs(ServiceLifetime.Scoped);

В отличие от переданного Func, вы не увидите большой разницы на первый взгляд:

RegisterAsWithFunc(sc.AddScoped);
1
321X 4 Фев 2022 в 12:02
1
Что ж, я бы предпочел это решение для реальной проблемы, но оно не отвечает на этот вопрос... который касается общей функции.
 – 
Sir Rufo
4 Фев 2022 в 11:45
Я знаю, но иногда думать о проблеме нужно нестандартно :-)
 – 
321X
4 Фев 2022 в 11:48
Но это сайт вопросов/ответов... чтобы получить ответы на вопрос. И настоящий ответ таков: «Нет, это невозможно в C#». Вы можете добавить другое решение, но вы должны ответить на заданный вопрос в этом разделе ответов.
 – 
Sir Rufo
4 Фев 2022 в 11:51
Я согласен с вами, что мой ответ мог быть немного лучше, и я улучшил его. В соответствии с часто задаваемыми вопросами также разрешено представлять жизнеспособную альтернативу, которой я считаю. Эта платформа также предназначена для обучения :-)
 – 
321X
4 Фев 2022 в 12:14

Да. Например, все операторы LINQ принимают универсальные функции Func. Подпись Enumerable.Where это:

Where<TSource>(this IEnumerable<TSource>, Func<TSource,Boolean>)

Методы регистрации промежуточного программного обеспечения .NET Core Dependency Injection также принимают общие функции для фабричных методов, например один из AddScoped:

AddScoped<TService,TImplementation> (
    this Microsoft.Extensions.DependencyInjection.IServiceCollection services, 
    Func<IServiceProvider,TImplementation> implementationFactory)

Вы можете использовать это для создания нового экземпляра службы с помощью настраиваемого фабричного метода.

0
Panagiotis Kanavos 4 Фев 2022 в 11:23