为什么System.IO.Path.Combine有4个重载?

时间:2010-12-16 16:28:59

标签: c# .net

在.NET 4中,System.IO.PathCombine方法有以下重载:

public static string Combine(params string[] paths)
public static string Combine(string path1, string path2)
public static string Combine(string path1, string path2, string path3)
public static string Combine(string path1, string path2, string path3, string path4)

第一个在.NET 4中添加,以支持任意数量的路径参数。第二个在早期版本中已经存在,所以我认为它是为了向后兼容而保留的。

但我很好奇其他重载的用途是什么。使用params的第一个方法签名是否已涵盖这些用例?

编辑:我现在相信答案是“因为并非所有语言都支持params(并且传递没有params支持的数组不方便)”。然而,stackoverflow蜂巢的想法似乎强烈反对。因此,作为妥协,我不接受任何答案。

6 个答案:

答案 0 :(得分:28)

我怀疑性能,因为你必须用params创建一个中间数组,加上遍历数组的开销等等。可能有一些内部等情况,其中有一个使用固定编号参数的好例子版本

<强>更新

我现在已经进行了性能测试以检查我的假设。这是我本来应该做的事情 - 我打破了自己的表演口号:

  

不要想 - 衡量。

我的假设并不完全正确,但并非完全错误。 4固定参数版本比4 params版本略慢,但3和2固定变量表现明显更好。

当前接受的答案的性能测试工具存在许多问题,表明性能完全支持params版本 - 这是不正确的:

  • 它使用DateTime.Now进行计时 - 总是使用秒表进行微基准测试,如DatTime.Now仅精确到~10ms-> 15ms。 There are endless articles on this
  • 测试仅涵盖4参数版本的情况 - 3和2参数版本怎么样?
  • 不考虑垃圾的产生和收集。一种方法在A-> B之间的直线上可能更快,但是它也可能产生大量垃圾,这些垃圾必须在某个阶段被清理。这是延迟的性能损失,但它仍然会影响性能,因此应予以考虑。
  • 确保参数具有实际值 - 将单个字符路径组合在一起吗?

我有以下性能结果,其中包括2,3和4个参数变体,其中可以看出2和3变化的性能明显更好,而4变化的性能稍差。固定数量参数版本总体上更快,其中3个是这个问题最重要的(自.Net 1.1以来存在2个参数变体)。

***2 Args***
params2:3018.44ms
params2:3007.61ms
params2:2988.52ms
params2:2992.33ms
params2:2995.89ms
args2  :1724.83ms
args2  :1723.97ms
args2  :1727.76ms
args2  :1720.42ms
args2  :1718.24ms
***3 Args***
params3:4168.37ms
params3:4169.61ms
params3:4165.63ms
params3:4161.51ms
params3:4153.61ms
args3  :3476.96ms
args3  :3483.40ms
args3  :3482.49ms
args3  :3595.15ms
args3  :3561.11ms
***4 Args***
params4:4992.71ms
params4:4985.51ms
params4:4995.63ms
params4:5002.47ms
params4:4993.99ms
args4  :4993.02ms
args4  :4992.93ms
args4  :4991.07ms
args4  :4993.04ms
args4  :4995.14ms

测试:

public void MeasurePathPerformance()
{
    const int TestIterations = 5;
    const string Root = "C:\\xxxxxxxxxx";
    string seg = new string('x', 10);
    string path = null;

    Action<string, Func<double>> test = (name, action) =>
    {
        for (int i = 0; i < TestIterations; i++)
        {
            Console.WriteLine("{0}:{1:F2}ms", name, action());
        }
    };

    Console.WriteLine("***2 Args***");
    Action p2 = () => path = Path.Combine(new[] { Root, seg });
    test("params2", () => TimeTest(p2));
    Action a2 = () => path = Path.Combine(Root, seg);
    test("args2  ", () => TimeTest(a2));

    Console.WriteLine("***3 Args***");
    Action p3 = () => path = Path.Combine(new[] { Root, seg, seg });
    test("params3", () => TimeTest(p3));
    Action a3 = () => path = Path.Combine(Root, seg, seg);
    test("args3  ", () => TimeTest(a3));

    Console.WriteLine("***4 Args***");
    Action p4 = () => path = Path.Combine(new[] { Root, seg, seg, seg });
    test("params4", () => TimeTest(p4));
    Action a4 = () => path = Path.Combine(Root, seg, seg, seg);
    test("args4  ", () => TimeTest(a4));

    Console.WriteLine(path);
}

[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.GC.Collect")]
private static double TimeTest(Action action)
{
    const int Iterations = 10 * 1000 * 1000;

    Action gc = () =>
    {
        GC.Collect();
        GC.WaitForFullGCComplete();
    };

    Action empty = () => { };

    Stopwatch stopwatch1 = Stopwatch.StartNew();

    for (int j = 0; j < Iterations; j++)
    {
        empty();
    }

    double loopElapsed = stopwatch1.Elapsed.TotalMilliseconds;

    gc();

    action(); //JIT
    action(); //Optimize

    Stopwatch stopwatch2 = Stopwatch.StartNew();

    for (int j = 0; j < Iterations; j++)
    {
        action();
    }

    gc();

    double testElapsed = stopwatch2.Elapsed.TotalMilliseconds;

    return (testElapsed - loopElapsed);
}

答案 1 :(得分:2)

对于不支持与c#类似的语言的语法shugar怎么样 - “params”关键字

更新

删除了性能问题,因为我只测量了4个参数版本,但没有测量真正快速的3和2参数版本

性能不是原因。 (见下面的基准)

基准

我测量了性能。令我惊讶的是阵列版本略胜一筹。

    [TestMethod]
    public void MeasurePathPerfomance()
    {
        // repeat several times to avoid jit-issues
        for (int j = 0; j < 10; j++)
        {
            {
                DateTime start = DateTime.Now;

                string result;
                for (int i = 0; i < 30000; i++)
                {
                    result = System.IO.Path.Combine("a", "b", "c", "d"); // use 4 parameter version
                }
                TimeSpan spend = DateTime.Now - start;
                System.Console.WriteLine("4 param : {0}", spend.TotalMilliseconds);
            }
            {
                DateTime start = DateTime.Now;

                string result;
                for (int i = 0; i < 30000; i++)
                {
                    result = System.IO.Path.Combine(new string[] { "a", "b", "c", "d" });
                }
                TimeSpan spend = DateTime.Now - start;
                System.Console.WriteLine("array[4] param : {0}", spend.TotalMilliseconds);
            }
        }
    }

结果

    4 param : 10.001
    array[4] param : 9.0009
    4 param : 12.0012
    array[4] param : 8.0008
    4 param : 12.0012
    array[4] param : 10.001
    4 param : 11.0011
    array[4] param : 9.0009
    4 param : 11.0011
    array[4] param : 11.0011
    4 param : 11.0011
    array[4] param : 9.0009
    4 param : 10.001
    array[4] param : 8.0008
    4 param : 10.001
    array[4] param : 9.0009
    4 param : 11.0011
    array[4] param : 9.0009
    4 param : 11.0011
    array[4] param : 9.0009

<击>

答案 2 :(得分:2)

一个可能的原因还可能是减少垃圾收集器的压力。每次调用方法时,params-array重载都会创建一个新数组。如果经常调用该方法,则会创建大量临时数组对象,从而增加垃圾收集器的压力。

答案 3 :(得分:1)

向后兼容性是我能想到的最好的理由。重载可能都是第一种方法。

答案 4 :(得分:1)

仅仅因为性能,最后3的性能比第一种方法更大。

如果你想知道实现,只需看看带反射器的mscorlib,你会发现在最后3个函数中性能会更好

答案 5 :(得分:1)

我认为原因是大多数程序员使用一个,两个,三个或四个值进行组合,而不是想要更好地实现数组而不是使用更多值。

示例

Sum(a , b); //fine
Sum(a , b , c);//fine
Sum(a , b , c , d);//fine
Sum(a , b , c , d ,....); //now I think you think everyone think  even Microsoft also thinks, its better to implement array here

// something like this
Sum(params var[] n);   

所以你会发现大部分方法都包含1,2,3,4个参数,然后是params