动态AND可选方法参数

时间:2013-02-21 22:22:06

标签: c# dynamic parameters optional-parameters

所以C#支持可选参数:

void MethodName(string param = "optional!") { }

动态方法参数:

void MethodName(dynamic param) { }

但不幸的是,你不能一起使用它们(可选的param值必须是常量):

void MethodName(dynamic param = new { p1 = "", p2 = 0M }) { }

我过去曾经使用过这样的东西:

void GenericAnonHandler<T>(IEnumerable<T> param)
{
    foreach(T item in param)
    {
        var typedItem = Cast(item, new { p1 = "", p2 = 0M });
        var p2 = typedItem.p2; // Hark, IntelliSense!
    }
}    
static T Cast<T>(object obj, T type)
{
    return (T)obj;
}
void CallingMethod()
{
    var list1 = new List<ThisType>() { ... };
    var list2 = new List<ThatType>() { ... };

    var anon1 = list1
        .Select(x => 
            new { p1 = x.sPropName, p2 = x.dPropName });

    var anon2 = list2
        .Select(x => 
            new { p1 = x.sPropName2, p2 = x.dPropName2 });

     var both = anon1.Concat(anon2);

     GenericAnonHandler(both);
}

但这是一个额外的工作和类型特定的编码,只要你知道动态类型应该是什么,新的类或只是使用动态会更容易..但动态不提供IntelliSense (并且合情合理地说)。

我更喜欢使用接口,但不能因为源类型(在此ex:ThisType,ThatType中)具有不同的属性名称,并且我无法控制它们(第三方程序集)。

然而,它们是部分类,所以我可以使用具有统一属性名称和不同属性名称的匿名类型的签名创建一个接口,在部分实现接口,为缺少的值创建虚拟属性'other'类型,然后根据类型从相应属性中提取值..

..但这也是太多的工作..特别是如果我正在全力以赴创建3个新项目(界面,2个局部)。将anon类型创建为真实类并将其从之前的2种类型中翻译起来会更简单。

所有这一切都要问是否有任何聪明的方法可以实现我想要的东西;可选的动态参数,允许intellisense工作?

我知道这是一个愚蠢的问题......基本上相当于:如何在没有实际定义的情况下定义一个类...只是想知道是否有任何向导除了Cast(T,类型)路线:)

2 个答案:

答案 0 :(得分:4)

这里的答案是:不要这样做。

简短定义就足够了:struct StrDec { public string p1; public decimal p2; }

随意使用大量这些小定义;最好用名字实际记录它们代表的内容。您将获得编译时检查,智能感知,并且您的代码将更具可读性,因为这是一种文档形式。您也可以使用Tuple,但我发现它们使您的代码可读性降低,特别是如果它们需要进入严格嵌套的通用参数规范中。尽管如此,元组仍然比动态+匿名类型黑客更好。

一个好的起点是......

  • 永远不要使用dynamic:这是最后的功能。
  • 永远不要使用可选参数:这也是最后的功能。

dynamic破坏了类型系统,实质上意味着你得到了静态类型语言(罗嗦)的不良部分而没有好的部分(快速,可靠,自我记录,智能感知)。这很少值得。

可选参数很糟糕,因为它们破坏了可封装性,这是自20世纪70年代以来的结构化编程101 - 包装可选参数意味着重复参数规范并重复默认值。此外,还有一个小的技术限制,即它们是由调用者解决的,而不是被调用者,除非你以相当不同寻常的方式部署dll,否则不会咬你(这种限制对.NET等大型库很重要) )。相反,请考虑(再次)小型,一次性使用structs

答案 1 :(得分:0)

我想我会把动态和可选部分扔掉,看起来你真的不需要它。创建一个新类型(类或结构,取决于您的需要):

public class ThirdType
{
    public string P1 { get; set; }
    public decimal P2 { get; set ; }

    // you may want to add constructors
}

然后创建一些mapper方法(扩展方法会很好)

public static class MapperExtensions 
{
    public static ThirdType ToThirdType(this ThisType obj)
    {
        return new ThirdType() { P1 = obj.sPropName, P2 = obj.dPropName };
    }

    public static ThirdType ToThirdType(this ThatType obj)
    {
        return new ThirdType() { P1 = obj.sPropName2, P2 = obj.dPropName2 };
    }
}

现在每当你打电话给你的方法时,

void MethodName(ThirdType param)
{
    // A.) do this...
    if (param == null)
        param = new ThirdType() { P1 = "", P2 = 0M };

}

// ... or B.) create an overload (preferable to me)
void MethodName()
{
   MethodName(new ThirdType() { P1 = "", P2 = 0M });
}

只需使用扩展方法即可。我认为这个代码更加清晰。

private void ExecuteForBoth(ThisType obj1, ThatType obj2) // dummy method, just for illustration
{
    MethodName(obj1.ToThirdType());
    MethodName(obj2.ToThirdType());
}

我没有intellisense写这个,抱歉打字错误。