2012-06-07 73 views
4

我想將一些代碼從Delphi移植到C#,並且我找到了一種我無法以合理方式實現的構造,同時遵守.NET Framework設計準則(我在問題的最後解決了這個問題)。很顯然,C#,Java,C++(以及許多其他語言)提供了方法/構造函數重載的含義,但是Delphi構造函數還可以有多個名稱。這樣,有可能寫一個直接表示的意圖代碼:C#中重載的構造函數類似於Delphi(具有多個名稱)

var 
    Data, ParI, ParD, Locl: TDataElement; 
begin 
    Data := TDataElement.Create('Element'); 
    ParI := TDataElement.CreateParam('IntElement', 22); 
    ParD := TDataElement.CreateParam('DblElement', 3.14); 
    Locl := TDataElement.CreateLocal('LocalElement'); 
    // ... use the above objects ... 
end; 
下面

簡化代碼:

unit DataManager; 

interface 

TDataElement = class 
    FName: string; 
    FPersistent: Boolean; 
public 
    constructor Create(AName: string); 
    constructor CreateParam(AName: string; DefaultInt: Integer); overload; 
    constructor CreateParam(AName: string; DefaultDouble: Double); overload; 
    constructor CreateLocal(AName: string); 
    property Name: string read FName;; 
    property Persistent: Boolean read FPersistent; 
end; 

implementation 

constructor TDataElement.Create(AName: string); 
begin 
    FName := AName; 
    FPersistent := True; 
    // ... other initialization ... 
end; 

constructor TDataElement.CreateParam(AName: string; DefaultDouble: Double); 
begin 
    Create(AName); 
    // ... use DefaultInt ... 
end; 

constructor TDataElement.CreateParam(AName: string; DefaultInt: Integer); 
begin 
    Create(AName); 
    // ... use DefaultDouble... 
end; 

constructor TDataElement.CreateLocal(AName: string); 
begin 
    Create(AName); 
    FPersistent := False; 
    // ... other code for local (non-persistent) elements ... 
end; 

特別是在C#構造函數必須具有相同的名稱作爲類,所以首先我試圖用枚舉來區分行爲。唉,我偶然發現了幾個問題跌跌撞撞:

    在每個構造
  • 第一個參數是同一類型(ElementKind)
  • 構造不容易辨認像德爾福(創建與CreateParam與CreateLocal)
  • 格外小心,需要在DataElement
  • 錯誤
  • 可能性,如指定ElementKind.DoubleParam並傳遞
  • 一個額外的布爾參數是必需的以處理局部元素整數值的子類
下面

第一次嘗試:

public enum ElementKind 
{ 
    Regular, IntParam, DoubleParam, Local 
} 

public class DataElement 
{ 
    private string FName; 
    public string Name { get { return FName; } } 

    private bool FPersistent; 
    public bool Persistent { get { return FPersistent; } } 

    public DataElement(ElementKind kind, string name) 
    { 
     FName = name; 
     // ugly switch :-(
     switch (kind) 
     { 
      case ElementKind.Regular: 
      case ElementKind.IntParam: 
      case ElementKind.DoubleParam: 
       FPersistent = true; 
       break; 
      case ElementKind.Local: 
       FPersistent = false; 
       break; 
     } 
     // ... other initialization ... 
    } 

    public DataElement(ElementKind kind, string name, int defaultInt) 
     : this(kind, name) 
    { 
     // ... use defaultInt ... 
    } 

    public DataElement(ElementKind kind, string name, double defaultDouble) 
     : this(kind, name) 
    { 
     // ... use defaultDouble ... 
    } 

    // Redundant "bool local" parameter :-(
    public DataElement(ElementKind kind, string name, bool local) 
     : this(kind, name) 
    { 
     // What to do when "local" is false ??? 

     // ... other code for local (non-persistent) elements ... 
    } 
} 

public class Program 
{ 
    public void Run() 
    { 
     DataElement data = new DataElement(ElementKind.Regular, "Element"); 
     DataElement parI = new DataElement(ElementKind.IntParam, "IntElement", 22); 
     DataElement parD = new DataElement(ElementKind.DoubleParam, "DblElement", 3.14); 
     DataElement locl = new DataElement(ElementKind.Local, "LocalElement"); 
    } 
} 

然後我試圖更加面向對象的方法來區分的類型構造,同時保持在運行相同的初始化代碼()方法:

public class ElementKind 
{ 
    public class RegularElement 
    { 
     internal RegularElement() { /* disallow direct creation */ } 
    } 
    public class IntParamElement 
    { 
     internal IntParamElement() { /* disallow direct creation */ } 
    } 
    public class DoubleParamElement 
    { 
     internal DoubleParamElement() { /* disallow direct creation */ } 
    } 
    public class LocalElement 
    { 
     internal LocalElement() { /* disallow direct creation */ } 
    } 

    public static readonly ElementKind.RegularElement Regular = new RegularElement(); 
    public static readonly ElementKind.IntParamElement IntParam = new IntParamElement(); 
    public static readonly ElementKind.DoubleParamElement DoubleParam = new DoubleParamElement(); 
    public static readonly ElementKind.LocalElement Local = new LocalElement(); 
} 

public class DataElement 
{ 
    private string FName; 
    public string Name { get { return FName; } } 

    private bool FPersistent; 
    public bool Persistent { get { return FPersistent; } } 

    protected DataElement(string name) 
    { 
     FName = name; 
     // ... other initialization ... 
    } 

    public DataElement(ElementKind.RegularElement kind, string name) 
     : this(name) 
    { 
     FPersistent = true; 
    } 

    public DataElement(ElementKind.IntParamElement kind, string name, int defaultInt) 
     : this(name) 
    { 
     FPersistent = true; 
     // ... use defaultInt ... 
    } 

    public DataElement(ElementKind.DoubleParamElement kind, string name, double defaultDouble) 
     : this(name) 
    { 
     FPersistent = true; 
     // ... use defaultDouble ... 
    } 

    public DataElement(ElementKind.LocalElement kind, string name) 
     : this(name) 
    { 
     FPersistent = false; 

     // ... other code for "local" elements ... 
    } 
} 

public class Program 
{ 
    public void Run() 
    { 
     DataElement data = new DataElement(ElementKind.Regular, "Element"); 
     DataElement parI = new DataElement(ElementKind.IntParam, "IntElement", 22); 
     DataElement parD = new DataElement(ElementKind.DoubleParam, "DblElement", 3.14); 
     DataElement locl = new DataElement(ElementKind.Local, "LocalElement"); 
    } 
} 

一切編譯和工作得很好。那麼我的問題在這裏? .NET Framework設計指南以及名爲Microsoft FxCop的工具。通過這個工具運行最後一個代碼後,我得到了多個突破問題(見下文)。

問題是:如何設計我的類以符合.NET設計指南和最佳實踐?

斷裂 - 確定性90% - 嵌套類型應該是不可見 - ElementKind + RegularElement 斷裂 - 確定性90% - 嵌套類型應該是不可見 - ElementKind + IntParamElement 斷裂 - 確定性90% - 嵌套類型應不可見 - ElementKind + DoubleParamElement 斷裂 - 確定性90% - 嵌套類型應該是不可見 - ElementKind + LocalElement

斷裂 - 確定性90% - 靜態成員的類型不應該有構造 - ElementKind

打破 - 確定性75% - 標識符不應包含類型名稱 - DataElement。#。ctor(ElementKind + IntParamElement,System.String,System.Int32)

打破 - 確定性75% - 標識符不應包含類型名稱 - DataElement 。#構造函數(ElementKind + DoubleParamElement,System.String,系統。雙人間)

非斷裂 - 確定性的25% - 不聲明只讀可變引用類型 - ElementKind#普通

非破 - 確定性的25% - 不聲明只讀可變引用類型 - ElementKind#IntParam

非斷裂 - 確定性的25% - 不聲明只讀可變引用類型 - ElementKind#DoubleParam

非斷裂 - 確定性的25% - 不聲明只讀可變引用類型 - ElementKind#本地

回答

7

對於初學者來說,我會繼續enum更換您的嵌套「ElementKind」 class

public enum ElementKind 
{ 
    RegularElement, 
    IntParamElement, 
    DoubleParamElement, 
    LocalElement 
} 

另外,我不認爲需要被映射到一個構造你的Delphi代碼。你可能會更好使用返回DataElement的靜態factory methods。例如:

public static DataElement Create(string name) 
{ 
    return new DataElement(ElementKind.Regular, name); 
} 

public static DataElement CreateParam(string name, int defaultInt); 
{ 
    return new DataElement(ElementKind.IntParam, name); 
    // ... use defaultInt ... 
} 

// similar to above 
public static DataElement CreateParam(string name, double defaultDouble); 
public static DataElement CreateLocal(string name); 

由於您使用工廠函數創建DataElement對象,因此應該使構造函數爲私有。

然後,您可以更新您的run()函數來使用這些如下:

public void Run() 
{ 
    DataElement data = DataElement.Create("Element"); 
    DataElement parI = DataElement.CreateParam("IntElement", 22); 
    DataElement parD = DataElement.CreateParam("DblElement", 3.14); 
    DataElement locl = DataElement.CreateLocal("LocalElement"); 
} 

更新:我包括你Run()功能建議的更改和修正的基本Create()工廠方法(我相信這是應該返回一個「Regular」DataElement)。

+0

工廠方法是我在發佈我的問題幾個小時後想到的。非常感謝確認:) 順便說一句。 ElementKind旨在成爲一個枚舉,實際上它是C#代碼的第一個版本。很高興知道我可以回覆它。 –

1

你可以有靜態工廠方法:

public class TDataElement { 
    private TDataElement(){} 

    public static TDataElement Create(string name) { 
     return new TDataElement { 
            FName = name, 
            FPersistent = true 
            // ... other initialization ... 
           }; 
    } 

    public static TDataElement CreateParam(string name, double defaultDouble){ 
     var element = Create(name); 
     // ... use DefaultInt ... 
     return element; 
    } 
//...  
} 

你可以這樣使用它:

Data = TDataElement.Create("Element"); 
ParI = TDataElement.CreateParam("IntElement", 22); 

這是一個衆所周知的做法。舉例來說,你必須在.NET框架的Image類工廠方法:var image = Image.FromFile("test.jpg");

0

我想,這Delphi代碼是很好的候選人是在C#泛型:

class DataElement 
{ 
    public string Name { get; set; } 
    public bool Persistent { get; set; } 

    public DataElement(/* ctor params */) 
    { 
    } 
} 

class DataElement<T> : DataElement 
{ 
    public DataElement(string name, T defaultValue /* other ctor params */) 
    : base(/* base ctor params */) 
    { 
    } 
} 

class IntElement : DataElement<int> {} 
class DoubleElement : DataElement<double> {} 

更多TDataElement細節將有用。

+0

使代碼通用是一個好主意。感謝您的建議,我會記住這一點。 'TDataElement'是一個類族的基類。其中一些環繞數字類型,一個包裝Unicode字符串,一些使用「複合」設計模式支持布爾型掩碼。把所有內容放在一個帖子中會太方便。 作爲@JonSenchyna建議的工廠方法是移植具有多個名稱的Delphi構造函數的正確解決方案。 –

0

最後我想出了以下通用實現,這要感謝@JonSenchyna和@Dennis的建議。

作爲一項額外的功能,我添加了IElementFactory接口,以表示實現子類的所有InitXyz()方法的要求 - 這在原始Delphi代碼中不存在。

我沒有在這個設計中唯一喜歡的是做DataElement()ByteElement()公共的默認構造(其中來自new()約束),以及InitXyz()方法(因爲他們是公衆IElementFactory接口的一部分)的要求。(!)

顯得多麼美麗,善意和類似於Delphi代碼的重構Program.Run()方法是:

public class Program 
{ 
    public static void Run() 
    { 
     ByteElement data = ByteElement.Create("Element"); 
     ByteElement parI = ByteElement.CreateParam("IntElement", 22); 
     ByteElement parD = ByteElement.CreateParam("DblElement", 3.14); 
     ByteElement locl = ByteElement.CreateLocal("LocalElement"); 
    } 
} 

而且現在的FxCop抱怨大約只有四廠的方法,但我想我可以忍受的。

斷裂 - 確定性95% - 不要在泛型類型中使用靜態成員 - DataElement`1#創建(System.String)

斷裂 - 確定性95% - 不要在泛型類型中使用靜態成員 - DataElement`1#CreateParam(System.String,System.Int32)

斷裂 - 確定性95% - 不要聲明上通用的靜態成員類型 - DataElement`1#CreateParam(System.String,System.Double)

斷裂 - 確定性95% - 不要在泛型類型中使用靜態成員 - DataElement`1#CreateLocal(System.String)