代码之家  ›  专栏  ›  技术社区  ›  Cheeso

下级调用者能否从使用C 4.0生成的程序集中的可选参数中获益?

  •  2
  • Cheeso  · 技术社区  · 15 年前

    假设我有一个现有的程序集,并且一些类有重载方法,其中一些重载的默认行为或值是假定的。我认为这是一个非常典型的模式;

    Type2 _defaultValueForParam2 = foo;
    Type3 _defaultValueForParam3 = bar;
    
    public ReturnType TheMethod(Type1 param1)
    {
       return TheMethod(param1, _defaultValueForParam2);
    }
    public ReturnType TheMethod(Type1 param1, Type2 param2)
    {
       return TheMethod(param1, param2, _defaultValueForParam3);
    }
    public ReturnType TheMethod(Type1 param1, Type2 param2, Type3 param3)
    {
       // actually implement the method here. 
    }
    

    我理解C中的可选参数应该让我将其合并为一个方法。如果我生成一个带有一些标记为可选的参数的方法,它将与程序集的下级调用程序一起工作吗?


    编辑 “工作”是指 下级呼叫者 这是一个用C 2.0或3.5编译器编译的应用程序,它可以用一个、两个或三个参数来调用该方法,就像我使用了重载一样,下层编译器不会抱怨。

    我确实想重构并消除我的库中的所有重载,但我不想强制使用重构库的下级调用程序提供每个参数。

    3 回复  |  直到 15 年前
        1
  •  1
  •   P Daddy    15 年前

    我还没有阅读过新语言标准的文档,但我认为您的4.0之前版本的呼叫者必须通过 全部的 声明的参数,就像现在一样。这是因为参数传递的工作方式。

    调用方法时,参数被推送到堆栈上。如果传递了三个32位参数,那么12个字节将被推到堆栈上;如果传递了四个32位参数,那么16个字节将被推到堆栈上。推送到堆栈上的字节数在调用中是隐式的:被调用方假定传递了正确数量的参数。

    因此,如果一个函数接受四个32位参数,它将查看堆栈中调用方返回地址之前的16个字节。如果调用方只传递了12个字节,那么在调用之前,被调用方将读取堆栈上已存在的内容的4个字节。它无法知道所有16个预期字节都没有通过。

    这就是它现在的工作方式。对于现有的编译器,这是不变的。

    要支持可选参数,必须执行以下两种操作之一:

    1. 调用者可以传递一个额外的值,该值显式地告诉被调用者在堆栈上推送了多少个参数(或字节)。然后,被调用方可以为任何省略的参数填充默认值。
    2. 调用方可以继续传递所有声明的参数,将默认值(将从被调用方的元数据中读取)替换为代码中省略的任何可选参数。然后被调用方从堆栈中读取所有参数值,就像现在一样。

    我怀疑它将如上文(2)所述实施。这与它在C++中是如何完成的(虽然C++缺少元数据,需要在头文件中指定默认参数),但是更有效的是选项(1),因为它都是在编译时完成的,并且不需要附加的值来推送到堆栈上,并且是最直接的实现。选项(2)的缺点是,如果默认值更改,则必须重新编译所有调用方,否则它们将继续传递旧的默认值,因为它们已被编译为常量。这类似于公共常量现在的工作方式。请注意,选项(1)不受此缺点的影响。

    选项(1)也不支持命名参数传递,因此给定的函数声明如下:

    static void Foo(int a, int b = 0, int c = 0){}
    

    可以这样称呼:

    Foo(1, c: 2);
    

    选项(1)可以修改为允许这样做,方法是使额外的隐藏值成为省略参数的位图,其中每个位表示一个可选参数。这任意地限制了一个函数可以接受的可选参数的数量,尽管考虑到这个限制至少是32,这可能不是一件坏事。然而,这确实使它极不可能是实际的实现。

    给定任一实现,调用代码 必须 了解可选参数的机制,以便忽略调用中的任何参数。此外,对于选项(1),必须传递一个额外的隐藏参数,而旧的编译器甚至不知道该参数,除非将其作为正式参数添加到元数据中。

        2
  •  1
  •   Robert Harvey    15 年前

    在C 4.0中,当省略可选参数时,将替换该参数的默认值,即:

    public void SendMail(string toAddress, string bodyText, bool ccAdministrator = true, bool isBodyHtml = false)
    { 
        // Full implementation here   
    }
    

    对于下级调用者,这意味着如果他们使用缺少参数的变体之一,C将 替换您为缺少的参数提供的默认值 . This article explains the process in greater detail .

    您现有的下级呼叫应该仍然有效,但是 you will have to recompile your clients in c# 4.0 .

        3
  •  0
  •   Thomas Levesque    15 年前

    好吧,我认为如果用一个可选参数的方法替换所有3个方法,那么使用库的代码仍然可以工作,但是需要重新编译。