You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is your feature request related to a problem? Please describe.
I often run into the scenario where there is a custom interface I need to substitute which is partially implemented by an existing type (in my case, it is usually custom collection-like interfaces that inherit (only) from IEnumerable<T>, IReadonlyList<T>, etc They are part of a public API.)
Describe the solution you'd like
In my test I would like to use a real collection (in this case, but it could be for any sort of type) as a substitute without having to manually configure each member to return/call the corresponding member on for example List<T> (which does not implement the custom collection, but it does have interfaces in common)
Describe alternatives you've considered
I tried the new feature Substitute.Substitute.ForTypeForwardingTo<TClass,Tinterface>(), but it throws an exception because List<T> does not directly implement the custom collection.
So far I use a workaround as a custom ICallHandler, but I suspect all the reflection inside of it will execute on every call, which is not optimal. Also, there is no checking if there is at least one interface the target and the substitute have in common, making it easy to introduce errors if the wrong interface is used to creating a substitute.
publicstaticclassSubstituteExtensions{publicstatic T ForwardCallsTo<T>(thisTsubst,objectinstance){
SubstitutionContext.Current.GetCallRouterFor(subst).RegisterCustomCallHandlerFactory(state =>new RedirectToInstanceHandler(instance));returnsubst;}privateclassRedirectToInstanceHandler:ICallHandler{privatereadonlyobject_instance;publicRedirectToInstanceHandler(objectinstance){_instance=instance;}public RouteAction Handle(ICallcall){// Look if the called method is implemented on the instance.varmethodInfo= call.GetMethodInfo();MethodInfomethodOnInstance=null;if(methodInfo.ReflectedType.IsInterface){if(call.Target()!=_instance// prevent stackoverflow caused by forwarding to itself&& methodInfo.ReflectedType.IsInstanceOfType(_instance))methodOnInstance=methodInfo;// otherwise, forward to interface implementation on other object}else// find implementing method called from the classmethodOnInstance= GetInterfaceDeclarationsForMethod(methodInfo).FirstOrDefault(i => i.DeclaringType.IsInstanceOfType(_instance));if(methodOnInstance!=null)// If so, forward the call to the corresponding method on the instance.return RouteAction.Return(methodOnInstance.Invoke(_instance, call.GetArguments()));return RouteAction.Continue();// If not, do nothing. (fallback to default)}}privatestaticIEnumerable<InterfaceMapping>GetAllInterfaceMaps(TypeaType)=>
aType.GetTypeInfo().ImplementedInterfaces
.Select(aType.GetInterfaceMap);privatestaticIEnumerable<MethodInfo>GetInterfaceDeclarationsForMethod(MethodInfomi)=>
GetAllInterfaceMaps(mi.ReflectedType).SelectMany(map => Enumerable.Range(0, map.TargetMethods.Length).Where(n => map.TargetMethods[n]==mi).Select(n => map.InterfaceMethods[n]));}
Is your feature request related to a problem? Please describe.
I often run into the scenario where there is a custom interface I need to substitute which is partially implemented by an existing type (in my case, it is usually custom collection-like interfaces that inherit (only) from
IEnumerable<T>
,IReadonlyList<T>
, etc They are part of a public API.)Describe the solution you'd like
In my test I would like to use a real collection (in this case, but it could be for any sort of type) as a substitute without having to manually configure each member to return/call the corresponding member on for example
List<T>
(which does not implement the custom collection, but it does have interfaces in common)Describe alternatives you've considered
Substitute.Substitute.ForTypeForwardingTo<TClass,Tinterface>()
, but it throws an exception becauseList<T>
does not directly implement the custom collection.ICallHandler
, but I suspect all the reflection inside of it will execute on every call, which is not optimal. Also, there is no checking if there is at least one interface the target and the substitute have in common, making it easy to introduce errors if the wrong interface is used to creating a substitute.Additional context
The text was updated successfully, but these errors were encountered: