在C#中,Java的匿名类的等价物是什么?

25

我正在尝试将一个用Java编写的SDK移植到C#。

在这个软件中有许多“处理程序”接口,带有几个方法(例如:attemptSomethingHandler具有success()和几个不同的失败方法)。然后,在调用类中匿名实现并实例化该接口,并将其传递给SomethingModel类的attemptSomething方法。这是一个异步方法,并且具有多个可能失败或调用另一个方法的位置(传递处理程序)。这样,attemptSomethingHandler的匿名实现可以引用调用attemptSomething的类中的私有方法。

在C#中,无法匿名实现接口。我可以显式地实现一个新类,但此实现将仅适用于此调用类,并且不用于其他任何内容。更重要的是,我将无法访问调用类中的私有方法,但我需要这些方法而又不想公开它们。

基本上,根据SomethingModel类方法中发生的情况,我需要从调用类中运行不同的代码。

我一直在研究委托,但这将需要传递与处理程序接口中的方法数量相同数量的委托(就我所知)。

在C#中应该如何处理这种情况?我觉得我错过了一种非常常见的编程策略。必须有一种简单、干净的方法来解决这个问题。


13
或许你可以附上你正在尝试移植的Java代码以及你目前的尝试,来阐明你的问题? - Rowland Shaw
2
你可以在内部类中实现接口。这样,你就可以访问调用类的私有成员。 - Dennis_E
1
这句话听起来几乎像是你想要一个私有嵌套类。 - Aron
你可以成为一名编译器。 - nicolas
很遗憾,.NET不支持匿名类。现在,如果一个重写的类只在单个方法中使用,那么需要定义一个在该方法之外的类。 - kap
显示剩余2条评论
3个回答

20

使用委托:

 void AttemptSomethingAsync(Action onSuccess, Action<string> onError1, Action onError2 = null) {
     // ...
 }

 // Call it using:

 AttemptSomethingAsync(onSuccess: () => { Yes(); }, onError1: (msg) => { OhNo(msg); });

或者,使用类

 class AttemptSomethingHandler {
     Action OnSuccess;
     Action<string> OnError1;
     Action OnError2;
 }

 void AttemptSomethingAsync(AttemptSomethingHandler handler) {
     // ...
 }

 // And you call it like
 AttemptSomethingAsync(new AttemptSomethingHandler() {
       OnSuccess = () => { Yes() };
 }); 

或者事件

public delegate void SuccessHandler();
public delegate void ErrorHandler(string msg);

class SomethingModel {
     public event SuccessHandler OnSuccess;
     public event ErrorHandler OnError1;

     public void AttemptSomethingAsync() {
         // ...
     }
}

// Use it like

var model = new SomethingModel();
model.OnSuccess += Yes;
model.AttemptSomethingAsync();

private void Yes() {
}

1
我来自Java,我认为这个答案比被接受的那个更好。 - mickey

19

在C#中,我们没有像Java那样的匿名类型。您可以创建一个包含字段的匿名类型,如下所示:

var myObject = new { Foo = "foo", Bar = 1, Quz = 4.2f }

但是这些类型无法放置方法,并且只能通过使用objectdynamic将它们传递到方法中(由于它们在编译时没有类型,因此是由编译器生成的)。

相反,在C#中,我们使用委托或Lambda表达式。

如果我正确理解了您的意思,您可以像这样实现一个嵌套的私有类:

interface IMyInterface
{
    void Foo();
}

class MyClass
{
    public void Bar()
    {
        var obj = new MyInterface();
        obj.Foo();
    }

    private class MyInterface : IMyInterface
    {
        public void Foo()
        {
            // stuff
        }
    }
}

现在MyClass可以创建一个实现IMyInterfaceMyInterface实例。正如评论者所提到的,MyInterface可以访问MyClass的成员(虽然你肯定想尝试并坚持使用两种类型的公共可访问成员)。

这个封装了“匿名”类(在这里使用Java术语使它更简单),这也意味着您潜在地可以返回MyInterface作为IMyInterface,而其余的软件将一无所知。这实际上是某些抽象工厂模式的工作原理。


基本上,我需要根据SomethingModel类方法中发生的情况从调用类运行不同的代码。

这听起来像是严重的耦合问题。哦,亲爱的!

我认为你的问题似乎需要重构。在C#中,你可以使用事件来解决这个问题(注意:可以,而不应该)。只需为每个方法的“分支点”设置一个事件。但是我必须说,这会使您的解决方案更难以预测和维护。

但是我建议您以一种不需要如此紧密耦合的方式架构您的解决方案。

您也可以尝试使用流水线模型,但我不确定如何自己实现它。我知道Jetty(或者是Netty?JBOSS的Java NIO)肯定使用了类似的模型。

您可能会发现,为了测试您的类的预期功能,抛出一些单元测试将使其更容易设计您的解决方案(TDD)。


1
尽管您肯定希望尝试并坚持使用两种类型的公共可访问成员,为什么? - weston
因为(几乎)没有匿名类的良好理由。@nicolas它们是XY问题的一个例子。匿名类唯一可接受的用途是在生成的代码中,即使在这种情况下,使用生成的类名称也是更好的选择。使用Java中匿名类的最大原因是因为没有lambda类型。 - Dan
@DanPantry 基于什么?F# 拥有它并且比 C# 设计更好,这与 lambda 或其他无关,你只是想在内联中实现一个接口。你不是编译器。 - nicolas
@nicolas 基于这样一个事实,你很少想要内联实现一个接口... F#具有结构类型,因此在那里使用它是有意义的。 - Dan
@DanPantry 我不明白结构类型与用户有什么关系。我有一个接口IQuack。我想要内联一个IQuack对象,就这样。接口是关于抽象的,没有理由不让人们抽象化。如果你对此有原则性的反对意见,那么你可能缺少一些概念。 - nicolas
显示剩余4条评论

2

您可以使用嵌套类来模拟匿名类,但是为了像Java一样使用嵌套类,您需要传递对外部类的引用。在Java中,所有嵌套和匿名类默认都有这个引用,只有静态类没有。

interface IMyInterface
{
    void Foo();
}

class MyClass
{
    public void Bar()
    {
        IMyInterface obj = new AnonymousAnalog(this);
        obj.Foo();
    }

    private class AnonymousAnalog : IMyInterface
    {
        public void Foo(MyClass outerThis)
        {
            outerThis.privateFieldOnOuter;
            outerThis.PrivateMethodOnOuter();
        }
    }

    ...
}

嵌套类可以通过使用 OuterClass.this 访问创建它们的父类实例。 - Samah
@Samah 你是什么意思?你在说Java吗?如果是的话,你是对的。但这个人正在解释C#。在C#中,你不能使用OuterClass.this - Jenix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接