2

所以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 方程序集)。

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

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

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

我知道这是一个愚蠢的问题..基本上相当于:如何在不实际定义的情况下定义一个类..只是想知道除了 Cast(T,Type) 路线之外是否有任何巫师在袖手旁观:)

4

2 回答 2

4

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

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

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

一个好的起点将是...

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

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

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

于 2013-02-21T22:37:45.990 回答
0

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

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

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

于 2013-02-21T23:04:47.857 回答