#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 описал в вопросе. @fooiey3. @GuruStron Да, вы правы насчет использования
strin&
вместоobject
. Я обновил ответ альтернативной реализацией, используя делегаты, а не выражения.
Ответ №2:
Как сказал Джонатан, используйте интерфейс:
public interface INameable
{
strin& Name { &et; }
}
Затем для второго класса, где Name2 — это имя свойства, добавьте свойство Name, которое возвращает Name2 в качестве получаемого значения.
Затем в общей подписи добавьте
where T : class, INameable
Редактировать
Ответ Джонатана, вероятно, то, что вы хотите, хотя он ведет себя не совсем так, как вы просили.
Комментарии:
1. в качестве дополнительного примечания: для замены имени: для
Account2
: явной реализации интерфейса:strin& INameable.Name =&&t; Name2;