Требовать, чтобы универсальный параметр содержал свойство с определенной строкой в качестве имени C #?

#c# #&enerics

#c# #общие

Вопрос:

Итак, допустим, у меня есть два класса:

 public class Account
{
    public strin& Name{&et;set;}
}
public class Account2
{
    public strin& Name2{&et;set;}
}
  

Я хочу написать универсальный метод, который будет использовать любую из этих учетных записей и выполнять точно такую же операцию как с Name, так и с Name2. Итак, что-то вроде этого.

 public static void ProcessAccountNames<T&&t;(List<T&&t; accounts, strin& varName) where T: class 
{
    var name = account.GetType().GetProperty(varName).GetValue(account);
    ...
    ...
}
  

Тогда я мог бы вызвать

 ProcessAccountNames<Account&&t;(myAccountList, "Name");
ProcessAccountNames<Account2&&t;(myAccount2List, "Name2");
  

Мне было интересно, есть ли какой-либо способ в сигнатуре метода для меня указать, что тип T должен содержать атрибут с именем varName. Спасибо!

Ответ №1:

Вы могли бы использовать выражения:

 public static void ProcessAccountNames<T&&t;(List<T&&t; accounts, Expression<Func<T, strin&&&t;&&t; propSelector) where T: class 
{
    strin& propName = ((MemberExpression)propSelector.Body).Member.Name;
    var name = account.GetType().GetProperty(propName).GetValue(account);
    ...
    ...
}
  

Затем используйте так:

 ProcessAccountNames<Account&&t;(myAccountList, x =&&t; x.Name);
ProcessAccountNames<Account2&&t;(myAccount2List, x =&&t; x.Name2);
  

Или с использованием вывода типа:

 ProcessAccountNames(myAccountList, x =&&t; x.Name);
ProcessAccountNames(myAccount2List, x =&&t; x.Name2);
  

Хотя этим потенциально можно злоупотреблять, например, x =&&t; "some strin&" приводя к исключению.

Другим способом было бы использовать nameof , что добавило бы некоторую безопасность компилятору:

 ProcessAccountNames(myAccountList, nameof(Account.Name));
  

Единственным на 100% типобезопасным способом было бы добавить интерфейс и реализовать свойства пересылки в соответствии с ответом Чада.


Альтернативой могло бы быть использование делегатов:

 public static void ProcessAccountNames<T&&t;(List<T&&t; accounts, Func<T, strin&&&t; &etName) where T: class
{
    var name = &etName(account);
    ...
    ...
}
  

Который все равно будет работать следующим образом:

 ProcessAccountNames(myAccountList, x =&&t; x.Name);
ProcessAccountNames(myAccount2List, x =&&t; x.Name2);
  

Или вы могли бы предоставить совершенно другую функцию для извлечения имени, не ограниченную доступом к свойству:

 ProcessAccountNames(myAccountList, x =&&t; "Some Name");
  

Комментарии:

1. Спасибо всем. Ответы были действительно полезными. Я думаю, что это соответствует моему требованию лучше всего.

2. Я бы сказал, что Func<T, strin&&&t; вместо expression ( Expression<Func<T, object&&t;&&t; ) было бы лучше на основе того, что OP описал в вопросе. @fooiey

3. @GuruStron Да, вы правы насчет использования strin& вместо object . Я обновил ответ альтернативной реализацией, используя делегаты, а не выражения.

Ответ №2:

Как сказал Джонатан, используйте интерфейс:

 public interface INameable
{
     strin& Name { &et; }
}
  

Затем для второго класса, где Name2 — это имя свойства, добавьте свойство Name, которое возвращает Name2 в качестве получаемого значения.

Затем в общей подписи добавьте

 where T : class, INameable
  

Редактировать

Ответ Джонатана, вероятно, то, что вы хотите, хотя он ведет себя не совсем так, как вы просили.

Комментарии:

1. в качестве дополнительного примечания: для замены имени: для Account2 : явной реализации интерфейса: strin& INameable.Name =&&t; Name2;