简体   繁体   中英

Require that a generic parameter contains a property with a specific string as a name C#?

So lets say I have two classes:

public class Account
{
    public string Name{get;set;}
}
public class Account2
{
    public string Name2{get;set;}
}

I want to write a generic method that will take in either one of these accounts and perform the exact same operation on both Name and Name2. So something like this.

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

Then I could call

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

I was wondering if there was any way In the method signature for me to specify that type T must contain an attribute named varName. Thanks!

Like Johnathan said, use an interface:

public interface INameable
{
     string Name { get; }
}

Then for the second class where Name2 is the name of the property, add a Name property that returns Name2 as its get value.

Then on the generic signature, add

where T : class, INameable

EDIT

Johnathan's answer is probably what you want, though it doesn't strictly behave the way you asked for.

You could use expressions:

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

Then use like this:

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

Or using type inference:

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

Although this could potentially be abused eg x => "some string" resulting in an exception.

Another way would be to use nameof , which would add some compiler safety:

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

The only 100% type-safe way would be to add an interface and implement forwarding properties as per Chad's answer.


An alternative would to be to use delegates:

public static void ProcessAccountNames<T>(List<T> accounts, Func<T, string> getName) where T: class
{
    var name = getName(account);
    ...
    ...
}

Which would still work like this:

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

Or you could provide a completely different function for retrieving a name, not restricted to property access:

ProcessAccountNames(myAccountList, x => "Some Name");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM