【问题标题】:dynamic AND optional method parameters动态 AND 可选方法参数
【发布时间】:2013-02-21 22:22:05
【问题描述】:

所以C#支持可选参数:

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

以及动态方法参数:

void MethodName(dynamic 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 (并且合理地如此)。

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

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

..但这也是太多的工作.. 特别是如果我正在努力创建 3 个新项目(界面,2 个部分)。将匿名类型创建为真正的类并从之前的 2 种类型转换它会更简单。

这就是询问是否有任何聪明的方法来完成我想要的;允许智能感知工作的可选动态参数?

我知道这是一个愚蠢的问题.. 基本上相当于:我如何定义一个类而不实际定义它.. 只是想知道除了 Cast(T,类型)路线:)

【问题讨论】:

标签: c# dynamic parameters optional-parameters


【解决方案1】:

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

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

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

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

  • 永远不要使用dynamic:这是不得已而为之的功能。
  • 切勿使用可选参数:这也是不得已的功能。

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

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

【讨论】:

  • 我不明白:可选参数如何破坏封装?
  • @svick:您如何为int Frobber(int x=1, string p="q", double y=3.5); 编写包装器?或者换一种说法,接受相同参数但返回2 * Frobber(...) 的函数的声明是什么?需要明确的是,我看到很多方法声明都比这长得多......
  • (我不想说每次使用可选参数都有问题,但这是一个坏习惯,会导致维护问题。这也是一种代码味道,因为它是“最简单”的方式编写一个违反 SRP 的函数,这又是一个维护问题。)
  • 我同意我不喜欢动态和可选参数。我忘了我问过这个问题,然后回过头来想哇我真的花了这么多时间试图避免编写 2 个方法来消耗 2 个应该相同但不同的对象吗?一句话,是的。我想我真的很无聊哈哈..
【解决方案2】:

我想我会放弃动态和可选部分,您似乎在这里并不需要它。创建一个新类型(类或结构,取决于您的需要):

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

    // you may want to add constructors
}

然后创建一些映射器方法(扩展方法就可以了)

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

我在没有智能感知的情况下写了这篇文章,抱歉有错别字。

【讨论】:

  • 这有点像我一直在做的事情,基本上是为了避免不得不写。特别是如果有很多这样的类型。 AutoMapper 将是一个合理封装的解决方案。
猜你喜欢
  • 2019-03-08
  • 2012-06-19
  • 2015-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-06
  • 2014-06-06
相关资源
最近更新 更多