反思有多慢

时间:2009-04-21 08:00:29

标签: c# performance reflection assemblies

我最近创建了一个接口层,以区分DataAccessProvider和业务逻辑层。 通过这种方法,我们可以随时通过更改Web / App.Config中的值来更改DataAccessProvider的选择。 (如果需要,可以提供更多细节。)

无论如何,要做到这一点,我们使用反射来完成我们可以工作的DataProvider类。

/// <summary>
/// The constructor will create a new provider with the use of reflection.
/// If the assembly could not be loaded an AssemblyNotFoundException will be thrown.
/// </summary>
public DataAccessProviderFactory()
{
    string providerName = ConfigurationManager.AppSettings["DataProvider"];
    string providerFactoryName = ConfigurationManager.AppSettings["DataProviderFactory"];
    try
    {
        activeProvider = Assembly.Load(providerName);
        activeDataProviderFactory = (IDataProviderFactory)activeProvider.CreateInstance(providerFactoryName);
    }
    catch
    {
        throw new AssemblyNotFoundException();
    }
}

但现在我想知道反思有多缓慢?

7 个答案:

答案 0 :(得分:75)

在大多数情况下:超过足够快。例如,如果您使用它来创建DAL包装器对象,则与连接到网络所需的时间相比,通过反射创建对象所花费的时间将是微不足道。因此优化这将是浪费时间。

如果你在紧密循环中使用反射,有一些技巧可以改进它:

  • 泛型(使用包装器where T : new()MakeGenericType
  • Delegate.CreateDelegate(对于类型化的委托;对构造函数不起作用)
  • Reflection.Emit - 铁杆
  • Expression(与Delegate.CreateDelegate类似,但更灵活,适用于构造函数)

但为了您的目的,CreateInstance完全没问题。坚持这一点,并保持简单。


编辑:虽然关于相对表现的观点仍然存在,虽然最重要的是“衡量它”,但仍然,我应该澄清一些上述内容。有时候...... 确实很重要。首先测量。但是,如果您发现 太慢,您可能希望查看类似FastMember的内容,它会在后台安静地执行所有Reflection.Emit代码,以便为您提供很简单的API;例如:

var accessor = TypeAccessor.Create(type);
List<object> results = new List<object>();
foreach(var row in rows) {
    object obj = accessor.CreateNew();
    foreach(var col in cols) {
        accessor[obj, col.Name] = col.Value;
    }
    results.Add(obj);
}

这很简单,但会很快。在具体的例子中,我提到了一个DAL包装器 - 如果你正在做这个,请考虑类似dapper之类的内容,它会在后台再次执行所有Reflection.Emit代码,以便尽可能快地提供给你使用API​​:

int id = 12345;
var orders = connection.Query<Order>(
    "select top 10 * from Orders where CustomerId = @id order by Id desc",
    new { id }).ToList();

答案 1 :(得分:17)

与非反射代码相比,它更慢。重要的是,如果它缓慢,但如果它的缓慢它重要的。例如,如果您在Web环境中使用反射实例化对象,其中预期的可靠性可以上升到10K,那么它将会很慢。

无论如何,它不好提前关注性能。如果事情变得缓慢,如果你正确地设计了东西,你总是可以加快它们的速度,这样你预期可能需要优化的部分就会被本地化。

如果您需要加速,可以查看这篇着名的文章:

Dynamic... But Fast: The Tale of Three Monkeys, A Wolf and the DynamicMethod and ILGenerator Classes

答案 2 :(得分:9)

答案 3 :(得分:5)

反思并不慢。通过反射调用方法比正常方式慢大约3倍。如果您只执行一次或在非关键情况下执行此操作,则没有问题。如果你在时间关键的方法中使用它10'000次,我会考虑改变实现。

答案 4 :(得分:5)

我想我会做一个快速测试,以证明与没有比较慢的反射。

有了反思

  • 通过遍历每个属性并匹配
  • 来实例化58个对象
  • 总时间: 52254纳秒

    while (reader.Read()) {
        string[] columns = reader.CurrentRecord;
        CdsRawPayfileEntry toAdd = new CdsRawPayfileEntry();
        IEnumerable<PropertyInfo> rawPayFileAttributes = typeof(CdsRawPayfileEntry).GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(CustomIndexAttribute)));
        foreach (var property in rawPayFileAttributes) {
            int propertyIndex = ((CustomIndexAttribute)property.GetCustomAttribute(typeof(CustomIndexAttribute))).Index;
            if (propertyIndex < columns.Length)
                property.SetValue(toReturn, columns[propertyIndex]);
            else
                break;
        }
    }
    

没有反思

  • 通过创建新对象实例化58个对象
  • 总时间: 868纳秒

        while (reader2.Read()) {
            string[] columns = reader2.CurrentRecord;
            CdsRawPayfileEntry toAdd = new CdsRawPayfileEntry() {
                ColumnZero = columns[0],
                ColumnOne = columns[1],
                ColumnTwo = columns[2],
                ColumnThree = columns[3],
                ColumnFour = columns[4],
                ColumnFive = columns[5],
                ColumnSix = columns[6],
                ColumnSeven = columns[7],
                ColumnEight = columns[8],
                ColumnNine = columns[9],
                ColumnTen = columns[10],
                ColumnEleven = columns[11],
                ColumnTwelve = columns[12],
                ColumnThirteen = columns[13],
                ColumnFourteen = columns[14],
                ColumnFifteen = columns[15],
                ColumnSixteen = columns[16],
                ColumnSeventeen = columns[17]
            };
        }
    

虽然不完全公平,因为反射还必须在通过反射创建新对象的基础上检索每个属性的特定属性58 * 18次,但它至少提供了一些视角。

答案 5 :(得分:3)

除了遵循其他答案中给出的链接并确保您没有编写“pathalogically bad”代码之外,对我而言,最好的答案是自己测试。

只有你知道你的脖子在哪里,你的反射代码将成为用户的次数,反射代码是否会处于紧密的循环中等等。你知道你的商业案例,有多少用户访问你的网站,有什么用途要求是。

但是,考虑到你在这里展示的代码片段,我的猜测是反射的开销不会是一个大问题。

VS.NET Web测试和性能测试功能应该使测量此代码的性能非常简单。

如果您不使用反射,您的代码会是什么样子?它有什么限制?如果删除反射代码,您可能无法忍受自己遇到的限制。可能值得尝试设计此代码而不进行反射,以确定它是否可行,或者替代方案是否可取。

答案 6 :(得分:0)

在我开始玩IoC之前,我做了类似的事情。我将使用Spring对象定义来指定数据提供程序 - SQL,XML或Mocks!