Я думаю, что лучшее, что вы можете сделать, это Dictionary
для сопоставления имен классов с лямбда-методами, которые обращаются к соответствующему члену. Это будет не так эффективно, как ваш if
/ else
(или лучше switch
/ case
) метод. ПРИМЕЧАНИЕ. Если ваш switch
достаточно длинный, компилятор может сгенерировать хеш-таблицу случаев для поиска места ветвления.
Мне напомнили, что это запрос LINQ to EF, и я обновил его для создания соответствующего Expression
.
Дано:
Dictionary<string, Expression<Func<Spell, string>>> AccessSpellClass = new Dictionary<string, Expression<Func<Spell, string>>>() {
{ "sor", s => s.sor },
{ "wiz", s => s.wiz },
{ "cleric", s => s.cleric },
};
И ExpressionVisitor
для замены Expression
s:
public static class ExpressionExt {
/// <summary>
/// Replaces an Expression (reference Equals) with another Expression
/// </summary>
/// <param name="orig">The original Expression.</param>
/// <param name="from">The from Expression.</param>
/// <param name="to">The to Expression.</param>
/// <returns>Expression with all occurrences of from replaced with to</returns>
public static Expression Replace(this Expression orig, Expression from, Expression to) => new ReplaceVisitor(from, to).Visit(orig);
}
/// <summary>
/// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor {
readonly Expression from;
readonly Expression to;
public ReplaceVisitor(Expression from, Expression to) {
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
}
Вы можете использовать его в своем методе:
public IEnumerable<Spell> GetSpellsForClass(string classname) {
if (AccessSpellClass.TryGetValue(classname, out var accessExpr)) {
Expression<Func<string,bool>> testExpr = x => !String.IsNullOrEmpty(x);
var newTestBody = testExpr.Body.Replace(testExpr.Parameters[0], accessExpr.Body);
var newTestExpr = Expression.Lambda<Func<Spell,bool>>(newTestBody, accessExpr.Parameters[0]);
return _context.Spells.Where(newTestExpr);
}
else
return Enumerable.Empty<Spell>();
}
Лучше всего, если вы создадите Attribute
, и тогда вы можете создать Dictionary
во время выполнения в начале вашей программы:
public class UserClassAttribute : Attribute {
}
public class Spell {
public string name { get; set; }
public string description { get; set; }
[UserClass]
public string sor { get; set; }
[UserClass]
public string wiz { get; set; }
[UserClass]
public string cleric { get; set; }
}
void BuildAccessSpellClass() {
var members = typeof(Spell).GetProperties();
AccessSpellClass = new Dictionary<string, Func<Spell, string>>();
foreach (var p in members) {
if (p.GetCustomAttribute(typeof(UserClassAttribute)) != null) {
var className = p.Name;
var parmS = Expression.Parameter(typeof(Spell), "s");
var body = Expression.MakeMemberAccess(parmS, p);
var accessExpr = Expression.Lambda<Func<Spell,string>>(body, parmS);
AccessSpellClass.Add(className, accessExpr);
}
}
}
ПРИМЕЧАНИЕ. Если вы не можете использовать пользовательский атрибут, вы можете изменить метод так, чтобы он брал список имен свойств и создавал словарные записи для этих свойств.