Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sequence contains no elements ParameterTypes.Last() #54

Open
sw1nn4 opened this issue Oct 14, 2020 · 1 comment
Open

Sequence contains no elements ParameterTypes.Last() #54

sw1nn4 opened this issue Oct 14, 2020 · 1 comment

Comments

@sw1nn4
Copy link

sw1nn4 commented Oct 14, 2020

ExtensionsForMethodInfo.GetPropertyFromMethod(Type t) is throwing an exception when called on a property getter.

The method name is returned as "IGenericStateSetter`1[Boolean].get_Value", therefore the substring(0,3) does not equal "get", as expected. Since it's a property getter method.GetParameterTypes() returns an empty list, so the call to Last() throws an invalid operation exception.

I'm not trying to intercept the method, but I presume a check is being performed as to whether it should be intercepted or not.
I'm also using a Ninject.MockingKernel to create test mocks for any missing bindings. (The issue happens regardless of which mocking framework is used, I think they all use Castle.Proxies)

I don't believe there's any way to specify not to try intercepting an activation?

Stack trace:

System.Core.dll!System.Linq.Enumerable.Last<System.Type>(System.Collections.Generic.IEnumerable<System.Type> source)
Ninject.Extensions.Interception.dll!Ninject.Extensions.Interception.Infrastructure.Language.ExtensionsForMethodInfo.GetPropertyFromMethod(System.Reflection.MethodInfo method, System.Type implementingType) Line 18	
Ninject.Extensions.Interception.dll!Ninject.Extensions.Interception.Planning.Strategies.InterceptorRegistrationStrategy.Execute(Ninject.Planning.IPlan plan) Line 47	
Ninject.dll!Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map<Ninject.Planning.Strategies.IPlanningStrategy>(System.Collections.Generic.IEnumerable<Ninject.Planning.Strategies.IPlanningStrategy> series, System.Action<Ninject.Planning.Strategies.IPlanningStrategy> action)	
Ninject.dll!Ninject.Planning.Planner.CreateNewPlan(System.Type type)	
Ninject.dll!Ninject.Planning.Planner.GetPlan(System.Type type)	
Ninject.dll!Ninject.Activation.Context.ResolveInternal(object scope)	
Ninject.dll!Ninject.Activation.Context.Resolve()	
Ninject.dll!Ninject.KernelBase.Resolve(Ninject.Activation.IRequest request, bool handleMissingBindings)	
Ninject.dll!Ninject.Planning.Targets.Target<System.__Canon>.ResolveWithin(Ninject.Activation.IContext parent)	
@szaliszali
Copy link

The problem occurs for all explicitly implemented properties In this case, the name does not start with get because it will look like Full.Name.Of.Interface.Type.get_Property.

Location:

reproduction:

interface IInterface
{
	int Property { get; }
}

class Class : IInterface
{
	int IInterface.Property => -6; // crash
	public int Property => -6; // no crash
}

class Program
{
	static void Main()
	{
		var t = typeof(Class);
		foreach (var method in t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
		{
			Console.WriteLine($"Name: '{method.Name}'");
			Console.WriteLine(method.GetPropertyFromMethod(t));
		}
	}
}

My first guess would be look for the last dot in Name first, and only then check for get prefix. Also, the length<4 condition must be revisited.

Naive attempt to fix the method:

public static PropertyInfo GetPropertyFromMethod(this MethodInfo method, Type implementingType)
{
	if (!method.IsSpecialName)
	{
		return null;
	}

	var fullName = method.Name.AsSpan();
	var lastPeriod = fullName.LastIndexOf('.');
	var interfaceNameWithPeriod = fullName[..(lastPeriod + 1)];
	var name = fullName[(lastPeriod + 1)..];

	if (name.Length < 4)
	{
		return null;
	}
	var isGetMethod = name.StartsWith("get", StringComparison.InvariantCulture);

	var returnType = isGetMethod ? method.ReturnType : method.GetParameterTypes().Last();
	var indexerTypes = isGetMethod ? method.GetParameterTypes() : method.GetParameterTypes().SkipLast(1);

	var propertyName = $"{interfaceNameWithPeriod}{name[4..]}";
	return implementingType.GetProperty(propertyName, DefaultBindingFlags, null, returnType, indexerTypes.ToArray(), null);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants