C#中重载的构造函数类似于Delphi(具有多个名称)

时间:2012-06-07 20:01:25

标签: c# delphi constructor overloading fxcop

我正在尝试将一些代码从Delphi移植到C#,并且我发现了一种我无法以合理的方式实现的构造,同时遵守.NET Framework设计指南(我在问题的最后处理)。< / p>

显然,C#,Java,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)
  • 构造函数不像Delphi(Create vs. CreateParam vs. CreateLocal)
  • 那样容易识别
  • 在DataElement的子类中需要格外小心
  • 错误的可能性,例如指定ElementKind.DoubleParam并传递整数值
  • 处理本地元素需要额外的bool参数

首次尝试如下:

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

然后我尝试了更多面向对象的方法来按类型区分构造函数,同时在Run()方法中保持相同的初始化代码:

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设计指南和最佳实践?

  

Breaking - Certainty 90% - 嵌套类型不应该是可见的 - ElementKind + RegularElement   打破 - 确定性90% - 嵌套类型不应该是可见的 - ElementKind + IntParamElement   打破 - 确定性90% - 嵌套类型不应该是可见的 - ElementKind + DoubleParamElement   打破 - 确定性90% - 嵌套类型不应该是可见的 - ElementKind + LocalElement

     

Breaking - Certainty 90% - 静态持有者类型不应该有构造函数 - ElementKind

     

Breaking - Certainty 75% - 标识符不应包含类型名称 - DataElement。#。ctor(ElementKind + IntParamElement,System.String,System.Int32)

     

打破 - 确定性75% - 标识符不应包含类型名称 - DataElement。#。ctor(ElementKind + DoubleParamElement,System.String,System.Double)

     

非中断 - 确定性25% - 不要声明只读可变引用类型 - ElementKind。#Regular

     

非破坏 - 确定性25% - 不要声明只读可变引用类型 - ElementKind。#IntParam

     

非破坏 - 确定性25% - 不要声明只读可变引用类型 - ElementKind。#DoubleParam

     

非破坏 - 确定性25% - 不要声明只读可变参考类型 - ElementKind。#Local

4 个答案:

答案 0 :(得分:7)

对于初学者,我会用[{1}}替换嵌套的“ElementKind”class

enum

此外,我认为您的Delphi代码不需要映射到构造函数。使用返回public enum ElementKind { RegularElement, IntParamElement, DoubleParamElement, LocalElement } 的静态factory methods可能会更好。例如:

DataElement

由于您正在使用工厂函数来创建DataElement对象,因此您应该将构造函数设为私有。

然后,您将更新Run()函数以使用它们,如下所示:

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

更新:我在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()工厂方法(我相信它应该返回“常规”{{1 }})。

答案 1 :(得分: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");

答案 2 :(得分: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的更多详细信息将非常有用。

答案 3 :(得分:0)

最后,由于@JonSenchyna的想法和@Dennis的建议,我提出了以下通用实现。

作为一个额外的功能,我在混合中添加了IElementFactory接口,表达了实现子类以获得所有InitXyz()方法的要求 - 这在原始Delphi代码中是不存在的。

我在此设计中唯一不喜欢的是制作DataElement()ByteElement() public(来自new()约束)的默认构造函数的要求,以及{ {1}}方法(因为它们是公共InitXyz()接口的一部分)。

IElementFactory

看看有多精美,善意和类似于Delphi代码(!)的重构public enum ElementKind { RegularElement, IntParamElement, DoubleParamElement, LocalElement } public interface IElementFactory<TElement> { void InitElement(string name); void InitParam(string name, int defaultValue); void InitParam(string name, double defaultValue); void InitLocal(string name); } public class DataElement<TElement> where TElement : class, IElementFactory<TElement>, new() { private ElementKind FKind; public ElementKind Kind { get { return FKind; } } private string FName; public string Name { get { return FName; } } private int FDefaultInt; protected int DefaultInt { get { return FDefaultInt; } set { FDefaultInt = value; } } private double FDefaultDouble; protected double DefaultDouble { get { return FDefaultDouble; } set { FDefaultDouble = value; } } protected DataElement() { } public virtual void InitElement(ElementKind kind, string name) { FKind = kind; FName = name; } public static TElement Create(string name) { TElement obj = new TElement(); obj.InitElement(name); return obj; } public static TElement CreateParam(string name, int defaultValue) { TElement obj = new TElement(); obj.InitParam(name, defaultValue); return obj; } public static TElement CreateParam(string name, double defaultValue) { TElement obj = new TElement(); obj.InitParam(name, defaultValue); return obj; } public static TElement CreateLocal(string name) { TElement obj = new TElement(); obj.InitLocal(name); return obj; } } public class ByteElement : DataElement<ByteElement>, IElementFactory<ByteElement> { public ByteElement() { } public void InitElement(string name) { base.InitElement(ElementKind.RegularElement, name); } public void InitParam(string name, int defaultValue) { base.InitElement(ElementKind.IntParamElement, name); // Range checking if ((defaultValue >= Byte.MinValue) && (defaultValue <= Byte.MaxValue)) { DefaultInt = defaultValue; } } public void InitParam(string name, double defaultValue) { base.InitElement(ElementKind.DoubleParamElement, name); // Range checking if ((defaultValue >= Byte.MinValue) && (defaultValue <= Byte.MaxValue)) { DefaultDouble = defaultValue; } } public void InitLocal(string name) { base.InitElement(ElementKind.LocalElement, name); } } 方法是:

Program.Run()

现在FxCop只抱怨了四种工厂方法,但我认为我可以接受这种方法:

  

Breaking - Certainty 95% - 不要在泛型类型上声明静态成员 - DataElement`1。#Create(System.String)

     

Breaking - Certainty 95% - 不要在泛型类型上声明静态成员 - DataElement`1。#CreateParam(System.String,System.Int32)

     

Breaking - Certainty 95% - 不要在泛型类型上声明静态成员 - DataElement`1。#CreateParam(System.String,System.Double)

     

Breaking - Certainty 95% - 不要在泛型类型上声明静态成员 - DataElement`1。#CreateLocal(System.String)