如何使用C#中的委托实现使用接口和实现类的类似设计(来自Java)

Mic*_*lak 5 c#

我在Java(7)应用程序中有以下设计:

有一种方法可以传递某些类型和对象的集合,我将其称为"谓词",用于过滤给定的集合.

Predicate是一个名为test的方法的接口 - 它接受一个对象并返回一个布尔值.

在这种情况下:

  • 任何人都可以通过创建实现Predicate的类来编写它自己的Predicate接口实现
  • 任何人都可以通过在调用过滤方法期间创建匿名实现来编写自己的Predicate接口实现
  • 有预定义的一组实现Predicate的类,它涵盖了许多标准情况,因此在大多数情况下不需要编写新的实现.
  • 还有一个实用程序类提供了几个静态方法,如and,or,allOf,anyOf,它允许将作为输入的谓词组合成一个新的谓词(在内部它将返回新谓词的匿名实现)

现在我想在C#(4.0)应用程序中有类似的设计.我可以看到两种方式 - 通过模仿Java设计,或通过将Predicate更改为委托:

我可以尝试模仿Java设计,但是:

  • 据我所知,在C#中没有匿名的接口实现(所以在调用filter方法时没有创建新的谓词,而我的util类不必基于匿名实现,但第二个不是真正的问题)
  • 我觉得这不是C#这样做的事情

我可以尝试使用委托来实现类似的设计.我的过滤方法将采用对象和谓词委托的集合.在这种情况下:

  • 当一个人能够编写一些"实现"委托的类型时,我不知道如何实现这种情况.我想象有人会用类似于委托签名的方法编写类的情况 - 我对这个策略感觉不好,因为如果我有实现的接口 - 编译器会强迫我有正确的签名 - 因为我不能"扩展"或"实现"委托 - 只有当我尝试将"引用"传递给我的过滤方法时,编译器才会告诉我错了(当然它仍然是编译时错误).
  • 任何人都可以通过传递lambda或指向一些匹配Predicate委托的签名方法来编写它自己的Predicate接口实现.所以这里将与Java相同
  • 当我有预定义的谓词集时,我不知道如何实现这种情况.从技术角度来看,我当然可以看到这很简单 - 例如,我可以使用静态方法编写一个类来匹配委托的签名 - 每个方法都将覆盖一个谓词.但我觉得这种方法有点不一致 - 因为会有一个地方有预定义的谓词,而用户的谓词将在另一个地方定义(在Java中,预定义和用户定义的谓词都将是实现接口的类)
  • 如果Predicate是委托 - 我也可以编写一些可以通过多种方式组合谓词的Util类.这与Java中的相同

我的问题是 - 您认为在Java中实现相同(或尽可能相似)设计的最佳方式是什么,这将被视为正确,干净的C# - 这样做的方法?

我在Java编程方面有几年的经验,但在C#中不到一年的经验,所以我理解我可能看到的一些问题在C#世界中根本不存在或根本不被视为问题.

编辑:这是最简单的(我认为......)我的Java代码如何工作的可能示例:

我的"域名"对象:

public class Person {

    private final String firstName;
    private final String secondName;

    public Person(String firstName, String secondName) {
        this.firstName = firstName;
        this.secondName = secondName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getSecondName() {
        return secondName;
    }
}
Run Code Online (Sandbox Code Playgroud)

过滤类:

public class Filter {
    public Collection<Person> filter(Collection<Person> collection, Predicate predicate) {
        Collection<Person> result = new LinkedList<Person>();
        for(Person person: collection) {
            if(predicate.test(person)) {
                result.add(person);
            }
        }
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

谓词界面:

public interface Predicate {
    boolean test(Person person);
}
Run Code Online (Sandbox Code Playgroud)

两个简单的预定义实现

public class FirstNameStartsWithPredicate implements Predicate {

    private final String startsWith;

    public FirstNameStartsWithPredicate(String startsWith) {
        this.startsWith = startsWith;
    }

    public boolean test(Person person) {
        return person.getFirstName().startsWith(startsWith);
    }
}

public class LastNameEndsWithPredicate implements Predicate {

    private final String endsWith;

    public LastNameEndsWithPredicate(String endsWith) {
        this.endsWith = endsWith;
    }

    public boolean test(Person person) {
        return person.getSecondName().endsWith(endsWith);
    }
}
Run Code Online (Sandbox Code Playgroud)

实用类:

public final class PredicateUtils {

    public static Predicate and(final Predicate first, final Predicate second) {
        return new Predicate() {
            public boolean test(Person person) {
                return first.test(person) && second.test(person);
            }
        };
    }

    public static Predicate or(final Predicate first, final Predicate second) {
        return new Predicate() {
            public boolean test(Person person) {
                return first.test(person) || second.test(person);
            }
        };
    }

    public static Predicate allwaysTrue() {
        return new Predicate() {
            public boolean test(Person person) {
                return true;
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,使用示例:

Collection<Person> persons = Arrays.asList(
        new Person("John", "Done"),
        new Person("Jane", "Done"),
        new Person("Adam", "Smith")
);

Filter filter = new Filter();

// Predefined predicates
filter.filter(persons, new FirstNameStartsWithPredicate("J"));
filter.filter(persons, new LastNameEndsWithPredicate("e"));

// anonymous implementation
filter.filter(persons, new Predicate() {
    public boolean test(Person person) {
        return person.getFirstName().equals("Adam") && person.getSecondName().equals("Smith");
    }
});

// utility class
filter.filter(persons, PredicateUtils.allwaysTrue());
filter.filter(persons, PredicateUtils.and(new FirstNameStartsWithPredicate("J"), new LastNameEndsWithPredicate("e")));
filter.filter(persons, PredicateUtils.or(new FirstNameStartsWithPredicate("J"), new FirstNameStartsWithPredicate("A")));
Run Code Online (Sandbox Code Playgroud)

Che*_*hen 5

这在C#中是完全可行的.假设我们有一些用户和过滤器来过滤它们,一些过滤器是内置的,一些是由用户实现的.在这里,我们应该使用Java之类的接口而不是委托.

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
}

public interface IPredicate<T>
{
    bool IsValid(T entity);
}

public class UserPredicate : IPredicate<User>
{
    /* built-in predicates */
    public static UserPredicate Adult = new UserPredicate(u => u.Age >= 18);
    public static UserPredicate NoAddress = new UserPredicate(u => string.IsNullOrEmpty(u.Address));

    public Func<User, bool> Predicate { get; private set; }
    public UserPredicate(Func<User, bool> predicate)
    {
        this.Predicate = predicate;
    }

    bool IPredicate<User>.IsValid(User entity)
    {
        return this.Predicate(entity);
    }
}
Run Code Online (Sandbox Code Playgroud)

用户可以轻松添加这样的新谓词:

//user's code
var custom = new UserPredicate(MyCustomUserFilter);

bool MyCustomUserFilter(User u)
{
    //user's filter logic
}
Run Code Online (Sandbox Code Playgroud)

它与Java不同,因为在C#中,匿名类型无法实现接口.

而且将谓词"组合"成一个新的很容易.

var AdultWithNoAddress = new UserPredicate(u => UserPredicate.Adult.Predicate(u) 
    && UserPredicate.NoAddress.Predicate(u));
Run Code Online (Sandbox Code Playgroud)

编辑为了使谓词组合更清晰,您可以将组合逻辑放入谓词本身.

public interface IPredicate<T>
{
    bool IsValid(T entity);

    IPredicate<T> And(IPredicate<T> another);

    IPredicate<T> Or(IPredicate<T> another);
}

public class UserPredicate : IPredicate<User>
{
    public static UserPredicate Adult = new UserPredicate(u => u.Age >= 18);
    public static UserPredicate NoAddress = new UserPredicate(u => string.IsNullOrEmpty(u.Address));

    private Func<User, bool> _predicate;
    public UserPredicate(Func<User, bool> predicate)
    {
        _predicate = predicate;
    }

    public bool IsValid(User entity)
    {
        return _predicate(entity);
    }

    public IPredicate<User> And(IPredicate<User> another)
    {
        return new UserPredicate(u => this.IsValid(u) && another.IsValid(u));
    }

    public IPredicate<User> Or(IPredicate<User> another)
    {
        return new UserPredicate(u => this.IsValid(u) || another.IsValid(u));
    }
}

//usage
var AdultWithNoAddress = UserPredicate.Adult.And(UserPredicate.NoAddress);
Run Code Online (Sandbox Code Playgroud)