为什么我不能声明C#方法虚拟和静态?

时间:2008-10-29 20:17:41

标签: c# oop

我有一个辅助类,它只是一堆静态方法,并且希望子类化辅助类。某些行为是独特的,具体取决于子类,所以我想从基类调用虚方法,但由于所有方法都是静态的,我无法创建普通的虚方法(需要对象引用才能访问虚方法)。

这有什么办法吗?我想我可以使用单例.. HelperClass.Instance.HelperMethod()并不比HelperClass.HelperMethod()差。布朗尼指出任何可以指出支持虚拟静态方法的语言的人。

编辑:好的,我疯了。谷歌搜索结果让我觉得我不是那里的。

15 个答案:

答案 0 :(得分:42)

虚拟静态方法没有意义。如果我调用HelperClass.HelperMethod();,为什么我会期望调用一些随机子类'方法?当你有两个HelperClass子类时,解决方案真的会崩溃 - 你会使用哪一个?

如果你想拥有可重写的静态类型方法,你应该选择:

  • 单身,如果您希望全局使用相同的子类。
  • 传统类层次结构,如果您希望在应用程序的不同部分使用不同的行为,则使用工厂或依赖注入。

选择在您的情况下更有意义的解决方案。

答案 1 :(得分:32)

我不认为你疯了。您只想使用.NET当前不可能的内容。

如果我们谈论泛型,那么您对虚拟静态方法的请求就会非常有意义。 例如,我对CLR设计师的未来要求是允许我写这样的界面:

public interface ISumable<T>
{
  static T Add(T left, T right);
}

并像这样使用它:

public T Aggregate<T>(T left, T right) where T : ISumable<T>
{
  return T.Add(left, right);
}

但现在不可能,所以我这样做:

    public static class Static<T> where T : new()
    {
      public static T Value = new T();
    }

    public interface ISumable<T>
    {
      T Add(T left, T right);
    }

    public T Aggregate<T>(T left, T right) where T : ISumable<T>, new()
    {
      return Static<T>.Value.Add(left, right);
    }

答案 2 :(得分:15)

确实,这可以在Delphi中完成。一个例子:

type
  TForm1 = class(TForm)
    procedure FormShow(Sender: TObject);
  end;

  TTestClass = class
  public
    class procedure TestMethod(); virtual;
  end;

  TTestDerivedClass = class(TTestClass)
  public
    class procedure TestMethod(); override;
  end;

  TTestMetaClass = class of TTestClass;

var
  Form1: TForm1;

implementation

{$R *.dfm}

class procedure TTestClass.TestMethod();
begin
  Application.MessageBox('base', 'Message');
end;

class procedure TTestDerivedClass.TestMethod();
begin
  Application.MessageBox('descendant', 'Message');
end;


procedure TForm1.FormShow(Sender: TObject);
var
  sample: TTestMetaClass;
begin
  sample := TTestClass;
  sample.TestMethod;
  sample := TTestDerivedClass;
  sample.TestMethod;
end;

非常有趣。我不再使用Delphi,但我记得能够使用元类特征在自定义设计器画布上轻松创建不同类型的控件:控件类,例如。 TButton,TTextBox等是一个参数,我可以使用实际的元类参数调用适当的构造函数。

穷人的工厂模式:)

答案 3 :(得分:11)

只需使用常规静态方法,然后使用new关键字

隐藏它,即可达到相同的效果
public class Base 
{
    //Other stuff

    public static void DoSomething()
    {
        Console.WriteLine("Base");
    }
}

public class SomeClass : Base
{
    public new static void DoSomething()
    {
        Console.WriteLine("SomeClass");
    }
}
public class SomeOtherClass : Base
{
}

然后你可以调用这样的方法

Base.DoSomething(); //Base
SomeClass.DoSomething(); //SomeClass
SomeOtherClass.DoSomething(); //Base

答案 4 :(得分:8)

我来自Delphi,这是我在c#中非常想念的很多功能。 Delphi允许您创建类型化类型引用,并且您可以在需要父类类型的任何地方传递派生类的类型。这种对象类型的处理具有强大的实用性。特别是允许运行时确定元数据。我在这里混合语法非常糟糕,但在c#中它看起来像是:

    class Root {
       public static virtual string TestMethod() {return "Root"; }
    }
    TRootClass = class of TRoot; // Here is the typed type declaration

    class Derived : Root {
       public static overide string TestMethod(){ return "derived"; }
    }

   class Test {
        public static string Run(){
           TRootClass rc;
           rc = Root;
           Test(rc);
           rc = Derived();
           Test(rc);
        }
        public static Test(TRootClass AClass){
           string str = AClass.TestMethod();
           Console.WriteLine(str);
        }
    } 
  

会产生:   根   衍生

答案 5 :(得分:7)

静态方法存在于类的实例之外。它不能使用任何非静态数据。

虚拟方法将被重载函数“覆盖”,具体取决于实例的类型

所以你在静态和虚拟之间存在明显的矛盾。

这不是支持问题,而是一个概念。

更新:我在这里被证明是错的(见评论):

  

所以我怀疑你会发现任何支持虚拟的OOP语言   静态方法。

答案 6 :(得分:6)

你并不疯狂。你所指的叫做Late Static Binding;它最近被添加到PHP。有一个伟大的线程描述它 - 在这里:When would you need to use late static binding?

答案 7 :(得分:2)

Mart用'new'关键字做对了。 我实际上到这里是因为我需要这种功能,Mart的解决方案运行正常。事实上,我把它更好,并使我的基类方法抽象,以迫使程序员提供这个字段。

我的情景如下:

我有一个基类HouseDeed。每种房屋类型都源自HouseDeed必须有价格。

以下是部分基础HouseDeed类:

public abstract class HouseDeed : Item
{
    public static int m_price = 0;
    public abstract int Price { get; }
    /* more impl here */
}

现在让我们看看两个派生的房屋类型:

public class FieldStoneHouseDeed : HouseDeed
{
    public static new int m_price = 43800;
    public override int Price { get { return m_price; } }
    /* more impl here */
}

和...

public class SmallTowerDeed : HouseDeed
{
    public static new int m_price = 88500;
    public override int Price { get { return m_price; } }
    /* more impl here */
}

如您所见,我可以通过类型SmallTowerDeed.m_price和实例新的SmallTowerDeed()来访问房屋的价格。 作为抽象的,这种机制使得程序员难以为每个新派生的房屋类型提供价格。

有人指出“静态虚拟”和“虚拟”在概念上是如何相互矛盾的。我不同意。在此示例中,静态方法不需要访问实例数据,因此满足以下要求:(1)单独通过TYPE提供价格,以及(2)提供价格。

答案 8 :(得分:1)

我听说Delphi支持这样的事情。它似乎是通过使类对象实例为元类来实现的。

我没有看到它有效,所以我不确定它是否有效,或者它的重点是什么。

P.S。如果我错了,请纠正我,因为这不是我的域名。

答案 9 :(得分:1)

因为虚方法使用实例化对象的已定义类型来确定要执行的实现(与引用变量的声明类型相反)

...而静态,当然,如果甚至有一个实例化的类实例,那就完全不关心......

所以这些是不相容的。

最重要的是,如果你想根据实例的子类改变行为,那么方法应该是基类的虚方法,而不是静态方法。

但是,由于您已经拥有这些静态方法,现在需要覆盖它们,您可以通过以下方法解决问题: 将虚拟实例方法添加到简单地委托给静态方法的基类,然后根据需要覆盖每个派生子类中的那些虚拟实例包装器方法(不是静态方法)...

答案 10 :(得分:1)

实际上,可以使用关键字new代替virtual为方法或成员组合虚拟和静态。

以下是一个例子:

class Car
{
    public static int TyreCount = 4;
    public virtual int GetTyreCount() { return TyreCount; }
}
class Tricar : Car
{
    public static new int TyreCount = 3;
    public override int GetTyreCount() { return TyreCount; }
}

...

Car[] cc = new Car[] { new Tricar(), new Car() };
int t0 = cc[0].GetTyreCount(); // t0 == 3
int t1 = cc[1].GetTyreCount(); // t1 == 4

显然,TyreCount值可能已在重写的GetTyreCount方法中设置,但这样可以避免重复该值。可以从类和类实例中获取值。

现在有人可以找到该功能的真正智能用途吗?

答案 11 :(得分:1)

override方法提供从基类继承的成员的新实现。由覆盖声明覆盖的方法称为重写的基本方法。重写的基本方法必须与覆盖方法具有相同的签名。 您不能覆盖非虚拟或静态方法。重写的基本方法必须是虚拟,抽象或覆盖。

覆盖声明无法更改虚拟方法的可访问性。覆盖方法和虚方法都必须具有相同的访问级别修饰符。

您不能使用new,static或virtual修饰符来修改覆盖方法。

重写属性声明必须指定与继承属性完全相同的访问修饰符,类型和名称,并且重写的属性必须是虚拟,抽象或覆盖。

答案 12 :(得分:1)

有一种方法可以强制继承&#34;抽象静态&#34;抽象泛型类中的方法。见如下:

public abstract class Mother<T> where T : Mother<T>, new()
{
    public abstract void DoSomething();

    public static void Do()
    {
        (new T()).DoSomething();
    }

}

public class ChildA : Mother<ChildA>
{
    public override void DoSomething() { /* Your Code */ }
}

public class ChildB : Mother<ChildB>
{
    public override void DoSomething() { /* Your Code */ }
}

示例(使用前一位母亲):

public class ChildA : Mother<ChildA>
{
    public override void DoSomething() { Console.WriteLine("42"); }
}

public class ChildB : Mother<ChildB>
{
    public override void DoSomething() { Console.WriteLine("12"); }
}

public class Program
{
    static void Main()
    {
        ChildA.Do();  //42
        ChildB.Do();  //12
        Console.ReadKey();
    }
}

它不是那么好,因为你只能从一个抽象类继承而且它会要求你对你的new()实现宽容。

更多,我认为根据您继承的类的大小,内存将是昂贵的。 如果您遇到内存问题,则必须在新方法中设置每个属性/变量,这是一种非常有效的默认值。

答案 13 :(得分:0)

您可以使用新关键字

namespace AspDotNetStorefront
{
    // This Class is need to override StudioOnlineCommonHelper Methods in a branch
    public class StudioOnlineCommonHelper : StudioOnlineCore.StudioOnlineCommonHelper
    {
        //
        public static new void DoBusinessRulesChecks(Page page)
        {
            StudioOnlineCore.StudioOnlineCommonHelper.DoBusinessRulesChecks(page);
        }
    }
}

答案 14 :(得分:0)

可以通过在派生类中使用 new 关键字并在基类中抛出 NotSupportedException() 来模拟功能。

public class BaseClass{
    public static string GetString(){
        throw new NotSupportedException();   // This is not possible
    }
}

public class DerivedClassA : BaseClass {
    public static new string GetString(){
        return "This is derived class A";
    }
}

public class DerivedClassB : BaseClass {
    public static new string GetString(){
        return "This is derived class B";
    }
}

static public void Main(String[] args)
{
    Console.WriteLine(DerivedClassA.GetString());  // Prints "This is derived class A"
    Console.WriteLine(DerivedClassB.GetString());  // Prints "This is derived class B"
    Console.WriteLine(BaseClass.GetString());      // Throws NotSupportedException
}

由于在编译时无法检测到这种情况,并且 IntelliSense 不会建议应在派生类中实现此类功能,因此这是一个潜在的问题。

一个评论还建议使用NotImplemetedException()。 Microsoft 的文档表明不应处理这些异常中的任何一个,因此它们中的任何一个都应该起作用。

NotSupportedExceptionNotImplemetedException 之间的区别在 this blog 中进行了注释。