Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion bench/Autofac.Benchmarks/Autofac.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<OutputType>Exe</OutputType>
<GenerateProgramFile>false</GenerateProgramFile>
Expand Down
47 changes: 24 additions & 23 deletions bench/Autofac.Benchmarks/Benchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,28 @@ public static class Benchmarks
{
public static readonly Type[] All =
{
typeof(ChildScopeResolveBenchmark),
typeof(ConcurrencyBenchmark),
typeof(ConcurrencyNestedScopeBenchmark),
typeof(KeyedGenericBenchmark),
typeof(KeyedNestedBenchmark),
typeof(KeyedSimpleBenchmark),
typeof(KeylessGenericBenchmark),
typeof(KeylessNestedBenchmark),
typeof(KeylessNestedSharedInstanceBenchmark),
typeof(KeylessNestedLambdaBenchmark),
typeof(KeylessNestedSharedInstanceLambdaBenchmark),
typeof(KeylessSimpleBenchmark),
typeof(KeylessSimpleSharedInstanceBenchmark),
typeof(KeylessSimpleLambdaBenchmark),
typeof(KeylessSimpleSharedInstanceLambdaBenchmark),
typeof(DeepGraphResolveBenchmark),
typeof(EnumerableResolveBenchmark),
typeof(PropertyInjectionBenchmark),
typeof(RootContainerResolveBenchmark),
typeof(OpenGenericBenchmark),
typeof(MultiConstructorBenchmark),
typeof(LambdaResolveBenchmark),
};
typeof(ChildScopeResolveBenchmark),
typeof(ConcurrencyBenchmark),
typeof(ConcurrencyNestedScopeBenchmark),
typeof(KeyedGenericBenchmark),
typeof(KeyedNestedBenchmark),
typeof(KeyedSimpleBenchmark),
typeof(KeylessGenericBenchmark),
typeof(KeylessNestedBenchmark),
typeof(KeylessNestedSharedInstanceBenchmark),
typeof(KeylessNestedLambdaBenchmark),
typeof(KeylessNestedSharedInstanceLambdaBenchmark),
typeof(KeylessSimpleBenchmark),
typeof(KeylessSimpleSharedInstanceBenchmark),
typeof(KeylessSimpleLambdaBenchmark),
typeof(KeylessSimpleSharedInstanceLambdaBenchmark),
typeof(DeepGraphResolveBenchmark),
typeof(EnumerableResolveBenchmark),
typeof(PropertyInjectionBenchmark),
typeof(RootContainerResolveBenchmark),
typeof(OpenGenericBenchmark),
typeof(MultiConstructorBenchmark),
typeof(LambdaResolveBenchmark),
typeof(RequiredPropertyBenchmark),
};
}
61 changes: 61 additions & 0 deletions bench/Autofac.Benchmarks/RequiredPropertyBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Autofac.Core;

namespace Autofac.Benchmarks;

public class RequiredPropertyBenchmark
{
private IContainer _container;

[GlobalSetup]
public void Setup()
{
var builder = new ContainerBuilder();

builder.RegisterType<ServiceA>();
builder.RegisterType<ServiceB>();
builder.RegisterType<ConstructorComponent>();
builder.RegisterType<RequiredPropertyComponent>();

_container = builder.Build();
}

[Benchmark(Baseline = true)]
public void NormalConstructor()
{
_container.Resolve<ConstructorComponent>();
}

[Benchmark]
public void RequiredProperties()
{
_container.Resolve<RequiredPropertyComponent>();
}

public class ServiceA
{
}

public class ServiceB
{
}

public class ConstructorComponent
{
public ConstructorComponent(ServiceA serviceA, ServiceB serviceB)
{
ServiceA = serviceA;
ServiceB = serviceB;
}

public ServiceA ServiceA { get; }

public ServiceB ServiceB { get; }
}

public class RequiredPropertyComponent
{
required public ServiceA ServiceA { get; set; }

required public ServiceA ServiceB { get; set; }
}
}
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "7.0.100",
"version": "7.0.101",
"rollForward": "latestFeature"
},

Expand Down
6 changes: 6 additions & 0 deletions src/Autofac/Core/Activators/Reflection/BoundConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ internal BoundConstructor(ConstructorBinder binder, ParameterInfo firstNonBindab
/// </summary>
public ConstructorBinder Binder { get; }

/// <summary>
/// Gets a value indicating whether the constructor has the SetsRequiredMembers attribute,
/// indicating we can skip population of required properties.
/// </summary>
public bool SetsRequiredMembers => Binder.SetsRequiredMembers;

/// <summary>
/// Gets the constructor on the target type. The actual constructor used
/// might differ, e.g. if using a dynamic proxy.
Expand Down
10 changes: 10 additions & 0 deletions src/Autofac/Core/Activators/Reflection/ConstructorBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public ConstructorBinder(ConstructorInfo constructorInfo)
Constructor = constructorInfo ?? throw new ArgumentNullException(nameof(constructorInfo));
_constructorArgs = constructorInfo.GetParameters();

#if NET7_0_OR_GREATER
SetsRequiredMembers = constructorInfo.GetCustomAttribute<SetsRequiredMembersAttribute>() is not null;
#endif

// If any of the parameters are unsafe, do not create an invoker, and store the parameter
// that broke the rule.
_illegalParameter = DetectIllegalParameter(_constructorArgs);
Expand All @@ -44,6 +48,12 @@ public ConstructorBinder(ConstructorInfo constructorInfo)
/// </summary>
public ConstructorInfo Constructor { get; }

/// <summary>
/// Gets a value indicating whether the constructor has the SetsRequiredMembers attribute,
/// indicating we can skip population of required properties.
/// </summary>
public bool SetsRequiredMembers { get; }

/// <summary>
/// Gets the set of parameters to bind against.
/// </summary>
Expand Down
69 changes: 69 additions & 0 deletions src/Autofac/Core/Activators/Reflection/InjectableProperty.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) Autofac Project. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
using Autofac.Core.Resolving;
using Autofac.Core.Resolving.Pipeline;
using Autofac.Util;
using Autofac.Util.Cache;

namespace Autofac.Core.Activators.Reflection;

/// <summary>
/// Holds an instance of a known property on a type instantiated by the <see cref="ReflectionActivator"/>.
/// </summary>
internal class InjectableProperty
{
private readonly MethodInfo _setter;
private readonly ParameterInfo _setterParameter;

/// <summary>
/// Initializes a new instance of the <see cref="InjectableProperty"/> class.
/// </summary>
/// <param name="prop">The property info.</param>
public InjectableProperty(PropertyInfo prop)
{
Property = prop;

_setter = prop.SetMethod!;

_setterParameter = _setter.GetParameters()[0];

#if NET7_0_OR_GREATER
IsRequired = prop.GetCustomAttribute<RequiredMemberAttribute>() is not null;
#endif
}

/// <summary>
/// Gets the underlying property.
/// </summary>
public PropertyInfo Property { get; }

/// <summary>
/// Gets a value indicating whether this field is marked as required.
/// </summary>
public bool IsRequired { get; }

/// <summary>
/// Try and supply a value for this property using the given parameter.
/// </summary>
/// <param name="instance">The object instance.</param>
/// <param name="p">The parameter that may provide the value.</param>
/// <param name="ctxt">The component context.</param>
/// <returns>True if the parameter could provide a value, and the property was set. False otherwise.</returns>
public bool TrySupplyValue(object instance, Parameter p, IComponentContext ctxt)
{
if (p.CanSupplyValue(_setterParameter, ctxt, out var vp))
{
Property.SetValue(instance, vp(), null);

return true;
}

return false;
}
}
30 changes: 30 additions & 0 deletions src/Autofac/Core/Activators/Reflection/InjectablePropertyState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Autofac Project. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

namespace Autofac.Core.Activators.Reflection;

/// <summary>
/// Structure used to track whether or not we have set a property during activation.
/// </summary>
internal struct InjectablePropertyState
{
/// <summary>
/// Initializes a new instance of the <see cref="InjectablePropertyState"/> struct.
/// </summary>
/// <param name="property">The property.</param>
public InjectablePropertyState(InjectableProperty property)
{
Property = property;
Set = false;
}

/// <summary>
/// Gets the property.
/// </summary>
public InjectableProperty Property { get; }

/// <summary>
/// Gets or sets a value indicating whether this property has already been set.
/// </summary>
public bool Set { get; set; }
}
Loading