【问题标题】:C#: "Cannot create an instance of the static class"C#:“无法创建静态类的实例”
【发布时间】:2011-08-22 11:20:47
【问题描述】:

我正在将一些 Java 代码转换为 C#,偶然发现了以下奇怪的事情:

public interface IActivation {
    public abstract double func(double inputput);

    public static class S1 : IActivation {
        public double func(double input) {
            if (input > 0) return 1.0;
            return 0.0;
        }
    }
}

SomewhereElse(用法):

protected IActivation activation = new IActivation.S1();

看原始代码,很清楚这是什么意图:

  1. 声明一个接口并在其中嵌套该接口的几个静态实现(代码包含 IActivation 的其他实现,例如“S2”、“S3”等,此处省略)。
  2. 典型的使用场景是为该接口的一个特定实现分配一个变量。此外,顺便说一下,您需要实例化该变量,这些特定实现属于何处非常清楚 - 可以说,嵌套声明将进一步提高代码的可读性 (例如new IActivation.S1(); 明确表示 S1 是 IActivation 的特定实现)。

有趣的是,C# 不喜欢整个定义的方式:“无法创建静态类 'IActivation.S1”的实例。有谁知道如何重构该代码以保留 1. 和 2. 的方法?

【问题讨论】:

  • 为什么需要将类声明为静态的?
  • @Morvader: static,当应用于类时,在 Java 中的含义与在 C# 中的含义不同。
  • 添加了java,希望能从了解Java的人那里得到答案...

标签: c# java .net static


【解决方案1】:

在 Java 中,static 内部类对其封闭类型的成员没有隐式访问。在 C# 中,所有 嵌套类型无法访问其父类型的成员;无需在 C# 中添加修饰符即可触发此行为。

在 C# 中,static 类是 abstract sealed,因此它们不能被创建或派生——这与 Java 中的含义不同。此外,接口不能包含自己的类型声明。

试试这样的:

public interface IActivation {
    double Func(double inputput);
}

public class S1 : IActivation {
    public static readonly S1 Instance = new S1();

    private S1() { }

    public double Func(double input) {
        if (input > 0) return 1.0;
        return 0.0;
    }
}

如果您的目标是以某种“可读”方式提供默认实现(尽管我认为 IActivator.S1() 本质上更具可读性...),那么您可以创建一个静态工厂类:

public static class Activator
{
    public static S1 S1
    {
        get
        {
            return S1.Instance;

            // Or you could do this if you make the S1 constructor public:
            // return new S1();
        }
    }
}

但是,我对这更易读或更有帮助的说法提出异议。在特定类型的上下文中构造对象时,Visual Studio 将显示该类型的所有子类型。所以如果你这样做(|代表光标):

IActivator foo = new |

您应该获得当前范围内实现 IActivotor 的所有类的简洁列表。

【讨论】:

  • 没错,这是我考虑过的可能性之一,但它会打破“代码可读性”约束(第 2 点)。因为那样你就必须使用 protected IActivation activation = new S1(); 。最初的意图(这是我的猜想,因为代码不是来自我)是让某种形式的智能感知为您提供替代方案,如果您觉得必须使用其他实现。在您的情况下,每个人都必须猜测其他实现是什么......
  • 我看到您在我第一次发表评论后更新了您的答案,非常感谢。也许我应该澄清一下:在最初的程序员设计中,IActivation activation = new IActivation.| 将列出所有可用的 IActivation 实现,而不是其他(例如 IActivation2 的实现)。为了达到同样的效果,您需要使用工厂模式,恕我直言,这是相当大的写作开销。没有冒犯的意思,但我目前认为 John 的解决方案是最合适的,尽管它有缺点(项目中不存在)。
【解决方案2】:

不要将你的班级标记为static

【讨论】:

  • 仅此一项是行不通的,不幸的是,您在尝试时会收到 'S1': interfaces cannot declare types 错误。我认为归结为 Bas 和 cdhowie(见下文)所说的:“在 C# 中,接口不能声明内部类型。”
  • 我只回答“无法创建静态类的实例”。我建议 OP 读取编译器错误,更何况 C# 编译器会产生详细的错误描述。
  • 好吧,我问了一个你没有回答的具体问题。另外,您的“答案”会导致更多无法编译的代码,所以我不明白这一点。
【解决方案3】:

如果IActivation不一定是接口,可以转成抽象类

public abstract class IActivation
{
    public abstract double func(double inputput);

    public class S1 : IActivation
    {
        public override double func(double input)
        {
            if (input > 0) return 1.0;
            return 0.0;
        }
    }
}

这改变了代码的实际含义,但允许你说

var s1 = new IActivation.S1();

更新我能想到的主要问题是,如果你有一个类扩展了其他东西并实现了这个接口,它就不起作用(你不能从两个类继承)。然后,您可以创建一个接口和一个实现抽象类的抽象类,但这有点愚蠢。

另一种选择是

public interface IActivation {
    // ...
}

public class Activation {
    public class S1 : IActivation { 
        // ...
    }
}

优点是您将 IActivation 保留为接口,但您的命名空间中有另一个类。

在这两种情况下,您都没有直接从 Java 移植。

【讨论】:

  • 好主意!使用这种将接口转换为抽象类的方式时是否有任何缺点/副作用?即,与接口声明相比,有什么我无法完成的吗?
  • 无论如何,从 Java 到 C# 的 100% 移植是不可能的(在我的情况下),所以我并不担心(例如,在 C# 中的方法声明中没有“抛出”等效项)。解决方案 1 的唯一缺点是您失去了“实现接口”智能感知功能(多重继承问题很容易通过“查找所有引用”来验证,并且在我的情况下不存在)。我也很好奇“覆盖”是否对性能有任何负面影响(不应该有)。第二种解决方案基本上使用了与 1 相同的技术(即:嵌套类可以使用 C#,而不是接口类混合)。
  • 对于 C# 中的继承,您必须指定 override 或 new (msdn.microsoft.com/en-us/library/ms173153(v=vs.80).aspx)。在这种情况下,覆盖就是你想要的。
  • 我知道,这不是我所指的语法,而是覆盖的“内部实现”。例如,C++ 会在覆盖的情况下修改内部 vtable 方法指针。我只是想知道这是如何在 C# 中解决的,仅此而已。无论如何,我认为你的解决方案是最可行的 -> 接受。
  • 抱歉,我对内部实现了解的不够多,无法回答这个问题。
【解决方案4】:

错误消息本身很清楚,S1 类不能是静态的,因为您正在创建它的实例。从 S1 中删除 static 关键字。此外,访问修饰符和抽象修饰符在接口声明中无效。

在 C# 中,接口不能声明内部类型。

我的建议是使用工厂模式来获取正确的实例,而不是在您的界面中嵌套类型(这会增加耦合/依赖性)。

interface IActivation
{
    double func(double inputput);
}

public static class ActivationFactory
{
    IActivation GetImplA()
    {
        return new ImplA();
    }

    IActivation GetImplB()
    {
        return new ImplB();
    }
}

class ImplA : IActivation { }
class ImplB : IActivation { }

【讨论】:

    【解决方案5】:

    对每个 S'i' 实现使用 sigleton 模式,并按照上面 cdhowie 的描述撕裂 appart 接口和实现

    看来你不需要工厂——除非你的 S'i' 实例有自己的状态?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-03
      • 2011-02-27
      相关资源
      最近更新 更多