如何在运行时动态创建 Predicate<T>
How to create a Predicate<T> dynamically on runtime
我正在尝试在基础 class 中创建一个能够获取参数并动态创建 Predicate<T>
的方法。
这里是摘要class:
public abstract class Table<TResults>
where TResults : class
{
...
protected abstract List<TResults> Results { get; set; }
...
}
这是一个 class 实施 Table<TResults>
:
public class TrStudent
{
...
public string Name => // some code
...
public void Check()
{
// check implementation
}
}
public class TableStudents : Table<TrStudent>
{
...
protected override List<TrStudent> Results { get; set; }
...
public void Check_Student(string studentName) => Results.Find(r => r.Name == studentName).Check();
}
这是另一个 class 实现 Table<TResults>
:
public class TrAnswer
{
...
public string Name => // some code
public int Id => // some other code
...
public void Report()
{
// report implementation
}
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
...
protected override List<TrAnswer> Results { get; set; }
...
public void Report_Answer(string answerName, int answerId) => Results.Find(r => r.Name == answerName && r.Id == answerId).Report();
...
}
如果可能的话,我想做的是将 Table<TResults>
class 更新为:
public abstract class Table<TResults>
where TResults : class
{
...
protected abstract List<TResults> Results { get; set; }
...
protected abstract Predicate<T> Predicate { get; }
protected T Find(parameters) => Results.Find(parameters, Predicate);
}
所以我可以将派生的 classes 更新为:
public class TableStudents : Table<TrStudent>
{
...
public void Check_Student(string studentName) => Find(studentName).Check();
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
...
public void Report_Answer(string answerName, int answerId) => Find(answerName, answerId).Report();
}
但我不确定这是否可行,因为一些 lambda 比其他的接受更多的参数。
我已经检查过Predicate, Lambda expressions and also Expression class,我几乎可以肯定它可以完成,但我不知道从哪里开始。
感谢您的宝贵时间。
以下不是选项吗?
public abstract class Table<TResults> where TResults : class {
// ...
protected TResults Find(Predicate<TResults> predicate)
=> Results.Find(predicate);
}
public class TableStudents : Table<TrStudent> {
// ...
public void Check_Student(string studentName)
=> Find(r => r.Name == studentName).Check();
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
// ...
public void Report_Answer(string answerName, int answerId)
=> Find(r => r.Name == answerName && r.Id == answerId).Report();
}
我看不出在运行时经历麻烦来创建 Predicate<T>
有什么意义。
老实说,通常您的解决方案看起来有点过度设计。当每个子 class 无论如何都必须覆盖结果的 List
时,抽象 List.Find()
有什么意义?
那些子class确实拥有过滤结果所需的一切(即结果本身和谓词参数),但他们仍然必须要求抽象基础class过滤他们?
如果你多次需要谓词,你可以在每个子class中使用私有函数,returns Predicate<TResults>
给定参数:
public class TableStudents : Table<TrStudent> {
// ...
public void Check_Student(string studentName)
=> Find(FilterBy(studentName)).Check();
private static Predicate<TrStudent> FilterBy(string studentName)
=> r => r.Name == studentName;
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
// ...
public void Report_Answer(string answerName, int answerId)
=> Find(FilterBy(answerName, answerId)).Report();
private static Predicate<TrAnswer> FilterBy(string answerName, int answerId)
=> r => r.Name == answerName && r.Id == answerId;
}
不要因为这些私有 FilterBy()
函数具有相同的名称和相似的工作方式而被迫将它们提取到基础 class 中。 class 基础 classes 业务的子class 过滤器是如何运作的。 subclass 最了解如何过滤其结果,它可能会也可能不会使用一个或多个私有函数来创建它需要的 Predicate<T>
s。
请注意 FilterBy()
是 return 一个 Predicate<T>
的函数。 Predicate<T>
是一个函数对象,当您给它一个 T
值时,它 return 是一个 bool
。
它类似于像 bool MyPredicate(T value) {...}
这样的常规函数,只是您可以将它存储在变量中,传递它,甚至 return 它来自其他函数:
// create function object
var isAlphaNumeric = new Predicate<char>(c => char.IsLetter(c) || char.IsDigit(c));
// call function object with some values
Debug.Assert(isAlphaNumeric('a') == true);
Debug.Assert(isAlphaNumeric('&') == false);
这个更详细的 FilterBy()
版本可能会使与 isAlphaNumeric
的关系更清楚:
private static Predicate<TrStudent> FilterBy(string studentName) {
var hasName = new Predicate<TrStudent>(r => r.Name == studentName);
return hasName;
}
isAlphaNumeric
和hasName
的主要区别在于hasName
需要捕获studentName
参数的值通过将其存储在函数对象中。稍后,当 returned hasName
函数对象被 List.Filter()
调用一个或多个 TrStudent
对象时,该值将可用于名称比较。
顺便说一句,一个函数return调用一个函数(或将其他函数作为参数)被称为higher-order functions。 C# 从函数式编程中提取了它们,它们非常强大。例如,没有它们就不可能有 LINQ。但它们也可以替代一些面向对象的设计模式,如策略模式、模板方法模式、工厂模式,甚至依赖注入。
我正在尝试在基础 class 中创建一个能够获取参数并动态创建 Predicate<T>
的方法。
这里是摘要class:
public abstract class Table<TResults>
where TResults : class
{
...
protected abstract List<TResults> Results { get; set; }
...
}
这是一个 class 实施 Table<TResults>
:
public class TrStudent
{
...
public string Name => // some code
...
public void Check()
{
// check implementation
}
}
public class TableStudents : Table<TrStudent>
{
...
protected override List<TrStudent> Results { get; set; }
...
public void Check_Student(string studentName) => Results.Find(r => r.Name == studentName).Check();
}
这是另一个 class 实现 Table<TResults>
:
public class TrAnswer
{
...
public string Name => // some code
public int Id => // some other code
...
public void Report()
{
// report implementation
}
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
...
protected override List<TrAnswer> Results { get; set; }
...
public void Report_Answer(string answerName, int answerId) => Results.Find(r => r.Name == answerName && r.Id == answerId).Report();
...
}
如果可能的话,我想做的是将 Table<TResults>
class 更新为:
public abstract class Table<TResults>
where TResults : class
{
...
protected abstract List<TResults> Results { get; set; }
...
protected abstract Predicate<T> Predicate { get; }
protected T Find(parameters) => Results.Find(parameters, Predicate);
}
所以我可以将派生的 classes 更新为:
public class TableStudents : Table<TrStudent>
{
...
public void Check_Student(string studentName) => Find(studentName).Check();
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
...
public void Report_Answer(string answerName, int answerId) => Find(answerName, answerId).Report();
}
但我不确定这是否可行,因为一些 lambda 比其他的接受更多的参数。
我已经检查过Predicate, Lambda expressions and also Expression class,我几乎可以肯定它可以完成,但我不知道从哪里开始。
感谢您的宝贵时间。
以下不是选项吗?
public abstract class Table<TResults> where TResults : class {
// ...
protected TResults Find(Predicate<TResults> predicate)
=> Results.Find(predicate);
}
public class TableStudents : Table<TrStudent> {
// ...
public void Check_Student(string studentName)
=> Find(r => r.Name == studentName).Check();
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
// ...
public void Report_Answer(string answerName, int answerId)
=> Find(r => r.Name == answerName && r.Id == answerId).Report();
}
我看不出在运行时经历麻烦来创建 Predicate<T>
有什么意义。
老实说,通常您的解决方案看起来有点过度设计。当每个子 class 无论如何都必须覆盖结果的 List
时,抽象 List.Find()
有什么意义?
那些子class确实拥有过滤结果所需的一切(即结果本身和谓词参数),但他们仍然必须要求抽象基础class过滤他们?
如果你多次需要谓词,你可以在每个子class中使用私有函数,returns Predicate<TResults>
给定参数:
public class TableStudents : Table<TrStudent> {
// ...
public void Check_Student(string studentName)
=> Find(FilterBy(studentName)).Check();
private static Predicate<TrStudent> FilterBy(string studentName)
=> r => r.Name == studentName;
}
public class TableAnswers<TrAnswer> : Table<TrAnswer>
{
// ...
public void Report_Answer(string answerName, int answerId)
=> Find(FilterBy(answerName, answerId)).Report();
private static Predicate<TrAnswer> FilterBy(string answerName, int answerId)
=> r => r.Name == answerName && r.Id == answerId;
}
不要因为这些私有 FilterBy()
函数具有相同的名称和相似的工作方式而被迫将它们提取到基础 class 中。 class 基础 classes 业务的子class 过滤器是如何运作的。 subclass 最了解如何过滤其结果,它可能会也可能不会使用一个或多个私有函数来创建它需要的 Predicate<T>
s。
请注意 FilterBy()
是 return 一个 Predicate<T>
的函数。 Predicate<T>
是一个函数对象,当您给它一个 T
值时,它 return 是一个 bool
。
它类似于像 bool MyPredicate(T value) {...}
这样的常规函数,只是您可以将它存储在变量中,传递它,甚至 return 它来自其他函数:
// create function object
var isAlphaNumeric = new Predicate<char>(c => char.IsLetter(c) || char.IsDigit(c));
// call function object with some values
Debug.Assert(isAlphaNumeric('a') == true);
Debug.Assert(isAlphaNumeric('&') == false);
这个更详细的 FilterBy()
版本可能会使与 isAlphaNumeric
的关系更清楚:
private static Predicate<TrStudent> FilterBy(string studentName) {
var hasName = new Predicate<TrStudent>(r => r.Name == studentName);
return hasName;
}
isAlphaNumeric
和hasName
的主要区别在于hasName
需要捕获studentName
参数的值通过将其存储在函数对象中。稍后,当 returned hasName
函数对象被 List.Filter()
调用一个或多个 TrStudent
对象时,该值将可用于名称比较。
顺便说一句,一个函数return调用一个函数(或将其他函数作为参数)被称为higher-order functions。 C# 从函数式编程中提取了它们,它们非常强大。例如,没有它们就不可能有 LINQ。但它们也可以替代一些面向对象的设计模式,如策略模式、模板方法模式、工厂模式,甚至依赖注入。