簡體   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