c# - 效率 : Func<T>, 与 T 的实例

标签 c# .net generics functional-programming

最近我一直在尝试使用 Func<T>类,到目前为止我很喜欢它。然而,我注意到我越来越多地开始使用它,而不是实际使用 T 的实例。 ,所以想问一下; 使用Func<T> 的开销是多少?对比T ? 我知道这是一个有点笼统的问题,因为 T可以是任何东西,所以我想这个问题应该集中在传递一个函数而不是一个简单对象的实例的开销是多少?

为了论证,我们假设如下。

我们的模拟对象,T

public class Person
{
    private string _name = string.Empty;
    private int _age = 0;
    private bool _isMale = true;

    public Person(string name, int age, bool isMale)
    {
        this.Name = name;
        this.Age = age;
        this.IsMale = isMale;
    }

    public string Name
    {
        get { return this._name; }
        set { this._name = value; }
    }

    public int Age
    {
        get { return this._age; }
        set { this._age = value; }
    }

    public bool IsMale
    {
        get { return this._isMale; }
        set { this._isMale = value; }
    }
}

现在,假设我们在 IDictionary 上有一个漂亮的扩展方法,即通过键选择值,默认值。伪代码可以描述如下:
是在 KeyValuePair 集合中找到的键 是的,返回值 否,返回默认

选项 1. 我们的扩展方法使用 T 的实例

public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary source, TKey key, TValue @default)
{
    if (source.ContainsKey(key))
    {
        return source[key];
    }
    return @default;
}

// usage
var myValue = myDictionary.GetValueOrDefault("Richard", new Person());

选项 2。我们的扩展方法使用 Func<T> ...嗯,漂亮!

public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary source, TKey key, Func<TValue> defaultSelector)
{
    if (source.ContainsKey(key))
    {
        return source[key];
    }
    return defaultSelector();
}

// usage
var myValue = myDictionary.GetValueOrDefault("Richard", () => new Person("Richard", 25, true));

比较

比较上述选项,很明显两者都有潜在的好处。选项 1 稍微更容易阅读,但我目前喜欢 Func<T> 的用法,因此对我来说选项 2 似乎是理想的。我想我正在考虑它是一个延迟实例化的参数,它只在需要时执行,因此节省了效率,但我是对的吗?

最佳答案

这是我用于基准测试的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ConsoleApplication3
{
    using System.Collections;
    using System.Diagnostics;
    using System.Globalization;
    using System.Numerics;
    using System.Xml.Linq;

    public class Program
    {

        public class Person
        {
            private string _name = string.Empty;

            private int _age = 0;

            private bool _isMale = true;

            public Person(string name, int age, bool isMale)
            {
                this.Name = name;
                this.Age = age;
                this.IsMale = isMale;
            }

            public string Name
            {
                get
                {
                    return this._name;
                }
                set
                {
                    this._name = value;
                }
            }

            public int Age
            {
                get
                {
                    return this._age;
                }
                set
                {
                    this._age = value;
                }
            }

            public bool IsMale
            {
                get
                {
                    return this._isMale;
                }
                set
                {
                    this._isMale = value;
                }
            }
        }

        private static void Main(string[] args)
        {
            var myDictionary = new Dictionary<string, Person>();
            myDictionary.Add("notRichard", new Program.Person("Richard1", 26, true));
            myDictionary.Add("notRichard1", new Program.Person("Richard2", 27, true));
            myDictionary.Add("notRichard2", new Program.Person("Richard3", 28, true));
            myDictionary.Add("notRichard3", new Program.Person("Richard4", 29, true));
            // usage
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for(int i = 0; i < 100000000; i++)
            {
                var myValue = myDictionary.GetValueOrDefault("Richard", new Program.Person("Richard", 25, true));
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 100000000; i++)
            {
                var myValue = myDictionary.GetValueOrDefault("Richard", ()=> new Program.Person("Richard", 25, true));
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.ReadKey();
        }
    }
    public static class Ex
    {
        public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, TValue @default)
        {
            if (source.ContainsKey(key))
            {
                return source[key];
            }
            return @default;
        }
        public static TValue GetValueOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, Func<TValue> defaultSelector)
        {
            if (source.ContainsKey(key))
            {
                return source[key];
            }
            return defaultSelector();
        }


    }
}

调用每个扩展方法 100000000 次(没有找到入口,因此导致每次都执行 Func)给出以下结果:

T - 10352 毫秒

Func<T> - 12268 毫秒

调用每个扩展方法 100000000 次(并找到一个条目,因此根本不调用 Func)给出以下结果:

T - 15578 毫秒

Func<T> - 11072 毫秒

因此,哪个执行得更快取决于您保存了多少实例化以及每个实例化的成本。

通过重新使用默认的 person 实例稍微优化代码,为 T 提供 6809 毫秒和 7452 为 Func<T> :

            Stopwatch sw = new Stopwatch();
            var defaultPerson = new Program.Person("Richard", 25, true);
            sw.Start();
            for(int i = 0; i < 100000000; i++)
            {
                var myValue = myDictionary.GetValueOrDefault("Richard", defaultPerson);
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds);
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 100000000; i++)
            {
                var myValue = myDictionary.GetValueOrDefault("Richard", () => defaultPerson);
            }

因此,理论上(如果您不考虑实例化),在调用堆栈中节省一个跃点会给您带来一些性能提升,但在实践中,这种提升可以忽略不计。

关于c# - 效率 : Func<T>, 与 T 的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10728810/

相关文章:

c# - 在 .NET Core 中使用反射

java - 在运行时获取数组类名的安全方法是什么?

c# - 如何修复 json.net (Newtonsoft.Json) 运行时文件加载异常

c# - 这怎么不会在Silverlight中引起跨线程异常?

c# - 用于小型插件系统的简单 IoC 容器

c# - 如何为 SslStream.AuthenticateAsServer() 创建证书?

c# - 如何在 C# 中为接受 IComparable 的通用方法处理 double.NaN

c# - 使用 LINQ 在 C# 中合并两个对象列表

c# - C# 中的 ValueType 数组是堆还是栈?

c# - 默认缓冲区大小为 4096 有什么原因吗?