【问题标题】:How is Java's notion of static different from C#'s?Java 的静态概念与 C# 的概念有何不同?
【发布时间】:2009-02-04 18:26:40
【问题描述】:

我正在阅读 Josh Bloch 的书Effective Java,他建议在构建具有大量成员的对象时使用构建器设计模式。据我所知,这不是普通的设计模式,而是他的变体。我更喜欢它的外观,并试图在我正在编写的 C# Web 应用程序中使用它。这是用 Java 编写的代码,完美运行

public class Property { 

    private String title;
    private String area;

    private int sleeps = 0;

    public static void main(String[] args) {

        Property newProperty = new Property.Builder("Test Property").Area("Test Area").Sleeps(7).build();

    }

    private Property(Builder builder) {
        this.title = builder.title;
        this.area = builder.area;
        this.sleeps =builder.sleeps;
    }

    public static class Builder{
        private String title;
        private String area;

        private int sleeps = 0;

        public Builder (String title){
            this.title = title;
        }

        public Builder Area(String area){
            this.area = area;
            return this;
        }

        public Builder Sleeps(int sleeps){
            this.sleeps = sleeps;
            return this;
        }

        public Property build() {
            return new Property(this);
        }
    }   
}

当我把它放入我认为是 C# 的等价物时

 public class Property
    {
    private String title;
    private String area;

    private Property(Builder Builder)
    {
        title = Builder.title;
        area = Builder.area;
    }


    public static class Builder
    {
        // Required parameters
        private String title;
        private String area;

        // Optional parameters
        private int sleeps = 0;

        public Builder(String val)
        {
            this.title = val;
        }

        public Builder Area(String val)
        {
            this.area = val;
            return this;
        }

        public Builder Sleeps(int val)
        {
            this.sleeps = val;
            return this;
        }

        public Property build()
        {
            return new Property(this);
        }
    }
    }

然后我收到编译器警告。他们中的大多数“不能在静态类中声明实例成员”。

所以我的问题首先是我错过了什么?如果我遗漏了某些东西,我可以按照 Josh Bloch 推荐的方式但在 C# 中完成,最后,这一点也很重要,这是线程安全的吗?

【问题讨论】:

  • Effective Java 有什么“版本”:P?我有一个旧的,它不包括 Builder。我读到这是在 Josh 体验@Google 之后添加的新章节之一。

标签: c# java static


【解决方案1】:

public static class 在 Java 中意味着您定义了一个静态嵌套类。这意味着它在逻辑上包含在另一个类中,但它的实例可以在不引用它的外部类的情况下存在。非静态嵌套类称为“内部类”,它的实例只能相对于外部类的实例存在。

在 C# 中,static class 是不能被实例化的,因此不能有任何非静态成员。 Java 中没有与此构造直接等效的语言级别,但您可以通过仅提供私有构造函数来轻松防止 Java 类的实例化。

简短的 Java 回顾:

  • 在另一个类中定义的所有类都是“嵌套类”
  • 不是static 的嵌套类称为内部类
  • 内部类的实例只能相对于外部类的实例存在
  • static 嵌套类没有单独的名称
  • static 嵌套类在很大程度上独立于其外部类(某些特权访问除外)。

如果有 C# 大师告诉我们如何在 C#/.NET 中处理内部/嵌套类,我会很高兴。

【讨论】:

  • 我认为您的意思是 nested 而不是第 1 行中的 inner
  • "嵌套类分为静态和非静态两类。声明为静态的嵌套类简称为静态嵌套类。非静态嵌套类称为内部类。" -- 来自java.sun.com/docs/books/tutorial/java/javaOO/nested.html
  • 我不是大师,但是 c# 内部类总是可以独立于外部类的实例而存在。我不知道 c# 中的构造只允许内部类通过其外部类存在,除了可能将其设为私有以便只有外部类可以使用它。
【解决方案2】:

我认为,如果您将 Builder 创建为顶级类(这正是 Java 中的内容)并创建一个工厂方法来接收 builder 以保持构造函数私有(这反过来,如果需要,您可以返回子类实例)。

重点是让构建器执行创建对象所需的步骤。

所以(在不太了解 C# 的情况下,您可以尝试类似的方法)

// My naive C# attempt:P

public class Property 
{

    public static void main( String []args ) 
    {
         Property p = Property.buildFrom( new Builder("title").Area("area").Etc() )
    }
    public static Property buildFrom( Builder builder ) 
    {
        return new Propert( builder );
    }

    private Property ( Builder builder ) 
    {
        this.area = builder.area;
        this.title = builder.title;
        // etc. 
    }
}
public class Builder 
{
    public Builder ( String t ) 
    {
       this.title = t;
    }

    public Builder Area( String area )
    {
       this.area = area;
       return this;
    }
    // etc. 
}

将 Builder 作为属性的静态内部类的全部意义在于在两者之间创建高度耦合(就好像它们在哪里一样)。这就是 Builder 中的 build 方法调用私有“Property”构造函数的原因。

可能在 C# 中,您可以使用替代工件来创建相同的耦合。

【讨论】:

  • 我想我会尝试这种方式。首先,因为我仍然可以拥有必须通过构造函数设置的必需值和可以通过调用构建器创建的可选值的概念。
【解决方案3】:

saua 有正确的答案,但我想特别说明一下你的例子:

在 C# 版本中,您应该从内部类中删除 static 关键字。它与 Java 版本的含义不同,实际上它在 Java 版本中的作用是 C# 中内部类的正常行为。

【讨论】:

  • 有没有办法在 C# 中从 Java 中获得非静态内部类的效果?这样内部类的每个实例都有对其外部类实例的隐式引用?
  • 是的,我也想知道。
  • 据我所知,这是不可能的。当然,没有什么可以阻止您保持明确的引用,但没有捷径可走。
【解决方案4】:

在 Java 中,默认情况下,嵌套类与其包含类的特定实例相关联。嵌套类的实例可以访问包含实例的变量和方法。如果嵌套类具有“static”关键字,则它不与外部类的实例相关联。正是在这个意义上,Bloch 在 Builder 类上使用了“static”关键字。

“静态”在应用于 C# 中的嵌套类时意味着不同的含义。我不知道你会在 C# 中使用什么关键字,或者即使有必要。您是否尝试将 static 关键字排除在类定义之外?

在 Java 类定义中使用“静态”在 Effective Java 的第 18 条中讨论。

【讨论】:

  • 我认为这是 Java 中的标准定义:嵌套类可以是静态的,也可以是非静态的。静态嵌套类就是这样称呼的;非静态嵌套类称为内部类。
  • 解释此问题的 Java 教程链接:java.sun.com/docs/books/tutorial/java/javaOO/nested.html
  • 糟糕,术语有点小问题。
  • 我使用的是第 2 版,您可能指的是第 18 条中的第 1 版。在我的书中,它谈到了优先使用接口而不是抽象类。也许你的意思是第 22 项(对我来说 :))更喜欢静态成员类?
【解决方案5】:

我不确定 Java 对静态类声明做了什么,但在 C# 中,静态类是一个只有类级成员的类,根据定义,不能在实例中实现。这就像旧 VB 中 Class 和 Module 之间的区别。

【讨论】:

    【解决方案6】:

    我不知道为什么 C# 会抱怨,但我可以说代码是线程安全的。如果您同时创建两个或多个Property 实例,每个实例都在各自的线程中,您不会遇到任何问题。

    【讨论】:

      【解决方案7】:

      我将尝试删除 static 关键字。正如其他人已经建议的那样,我的另一个想法是将构建器类创建为顶级类。

      【讨论】:

      • 这正是你应该做的——不要让构建器成为顶级类。
      • @SMonkey:如果删除了 static 关键字,那么在不先创建属性的情况下将无法访问构建器。 :(
      【解决方案8】:

      要回答几个关于如何在 C# 中获取 Java 内部类行为的问题,似乎需要在内部类的构造函数中传递对封闭类的引用(来自快速谷歌 - C# 可能已经添加能力)。

      public class Outer 
      {
      
      ...
      void SomeMethod() {
          Inner             workerBee=new Inner(this);
          }
      ...
      
          class Inner
          private Outer outer;
          {
          Inner(Outer out) {
              outer=out;
              }
          }
      }
      

      所以 C# 只是明确了 Java 隐式所做的事情,包括明确需要引用来访问外部类的成员。

      就我个人而言,我从不喜欢 Java 对外部类成员的隐式访问,因为它似乎太容易出错并意外破坏封装 - 我几乎总是将我的内部类创建为静态并将它们传递给外部类的引用.

      【讨论】:

        【解决方案9】:

        假设您的类具有与构建器成员对应的可公开设置的属性,则您不需要 C# 中的 Bloch 构建器模式。您可以使用对象初始化器

        public class Property 
        { 
        
            public String Title {get; set};
            public String Area {get; set};
            public int Sleeps {get; set};
        
            public static void main(String[] args)
            {
        
                Property newProperty = new Property {Title="Test Property", Area="Test Area", Sleeps=7};
        
            }
        }
        

        如果您需要更多封装,这将是不可能的。

        【讨论】:

        • 我可能需要为某些字段添加重载方法以使用 id 或实际文本值,因此属性对我来说并不是那么好。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-24
        • 1970-01-01
        • 2013-04-02
        • 2014-06-30
        • 2017-01-13
        相关资源
        最近更新 更多