繁体   English   中英

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

[英]Why can't I declare C# methods virtual and static?

我有一个助手 class ,它只是一堆 static 方法,并且想子类化助手 class。 根据子类,某些行为是独一无二的,所以我想从基础 class 调用一个虚拟方法,但由于所有方法都是 static 我无法创建一个普通的虚拟方法(需要 ZA8CFDE6331BD59EB2AC96F8911CB6 引用才能访问虚拟方法)。

有没有办法解决? 我想我可以使用 singleton .. HelperClass.Instance.HelperMethod() 并不比 HelperClass.HelperMethod() 差多少。 任何可以指出一些支持虚拟 static 方法的语言的人都可以加分。

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

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

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

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

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

我不认为你疯了。 您只想使用.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);
    }

实际上,这可以在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等是一个参数,我可以使用实际的元类参数调用适当的构造函数。

那种穷人的工厂模式:)

只需使用常规静态方法然后使用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

我来自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);
        }
    } 

会产生:根源

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

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

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

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

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

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

你并不疯狂。 你所指的叫做Late Static Binding; 它最近被添加到PHP。 有一个伟大的线程描述它 - 这里: 你什么时候需要使用后期静态绑定?

实际上,可以使用关键字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方法中设置,但这可以避免重复该值。 可以从类和类实例中获取值。

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

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

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

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

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

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

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

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

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

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

所以这些是不相容的。

最重要的是,如果您想根据实例所在的子类更改行为,那么这些方法应该是基类的虚方法,而不是静态方法。

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

有一种方法可以从抽象泛型类强制继承“抽象静态”方法。 见如下:

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()实现宽容。

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

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

我的情况如下:

我有一个基类HouseDeed。 每个House类型都源自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)提供价格。

可以通过在派生类中使用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之间的区别在此博客中进行了评论。

您可以使用new关键字

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);
        }
    }
}

您很快就能在 C# 11 中!

来自教程:探索 C# 11 功能 - static 接口中的虚拟成员

C# 11 和 .NET 7 包括接口中的 static 虚拟成员 此功能使您能够定义包含重载运算符或其他 static 成员的接口。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM