反思有多慢

use*_*964 64 c# reflection performance 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*ell 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);
}
Run Code Online (Sandbox Code Playgroud)

这很简单,但会很快.在具体的例子中,我提到了一个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();
Run Code Online (Sandbox Code Playgroud)

  • 如果有人想看看反射发射如何用于访问字段(它不是太复杂)请参阅:http://sharpanalytics.blogspot.de/2012/08/dynamic-methods-for-accessing-fields.html (2认同)

maj*_*tor 17

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

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

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

动态...但快速:三只猴子的故事,一只狼,动态方法和ILGenerator类


Rub*_*ins 9

以下是一些可能有用的链接:

  • 不要被震惊.百万次迭代测得的最长时间为22秒.对于最坏的情况,每次呼叫22*微*秒.除非你创造了大量的这些物品,否则它真的不是什么大问题.当然,如果你*正在创建大量的这些对象,那么它可能是一个大问题,但正如Marc所说,它仍然会被数据库连接和查询时间所淹没.除非你知道它的性能至关重要,否则不要被"x次慢"文章吓到. (5认同)

Eny*_*yra 5

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


Lev*_*ler 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;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

没有反思

  • 通过创建一个新对象实例化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]
            };
        }
    
    Run Code Online (Sandbox Code Playgroud)

尽管反射还必须通过反射创建一个新对象,而且还需要58 * 18次检索每个属性的特定属性,但这并不完全公平,但这至少提供了一些视角。