Я думаю, что у меня психический блок, но может кто-нибудь просветить меня, как объединить эти два оператора LINQ в один?
/// <summary>
/// Returns an array of Types that implement the supplied generic interface in the
/// current AppDomain.
/// </summary>
/// <param name = "interfaceType">Type of generic interface implemented</param>
/// <param name = "includeAbstractTypes">Include Abstract class types in the search</param>
/// <param name = "includeInterfaceTypes">Include Interface class types in the search</param>
/// <returns>Array of Types that implement the supplied generic interface</returns>
/// <remarks>
/// History.<br/>
/// 10/12/2008 davide Method creation.<br/>
/// </remarks>
public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool includeAbstractTypes, bool includeInterfaceTypes)
{
// Use linq to find types that implement the supplied interface.
var allTypes = AppDomain.CurrentDomain.GetAssemblies().ToList()
.SelectMany(s => s.GetTypes())
.Where(p => p.IsAbstract == includeAbstractTypes
&& p.IsInterface == includeInterfaceTypes);
var implementingTypes = from type in allTypes
from intf in type.GetInterfaces().ToList()
where intf.FullName != null && intf.FullName.Contains(interfaceType.FullName)
select type;
return implementingTypes.ToArray<Type>();
}
Я избегаю IsAssignableFrom, поскольку он, похоже, терпит неудачу, если не предоставляет конкретный тип универсального интерфейса, поэтому я считаю, что использование Caparison FullName вместо IsAssignableFrom должно быть достаточным, например:
namespace Davide
{
interface IOutput<TOutputType> { }
class StringOutput : IOutput<string> { }
}
typeof (IOutput <>). FullName вернет "Davide + IOutput`1"
typeof (StringOutput) .GetInterfaces () [0] .FullName вернет «Davide + IOutput`1 [[System.String, mscorlib, Version = 2.0.0.0, Culture = нейтральный, PublicKeyToken = b77a5c561934e089]]»
Поэтому использования FullName.Contains должно быть достаточно





это будет делать:
public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool includeAbstractTypes, bool includeInterfaceTypes)
{
// Use linq to find types that implement the supplied interface.
var allTypes = AppDomain.CurrentDomain.GetAssemblies().ToList()
.SelectMany(s => s.GetTypes())
.Where(p => p.IsAbstract == includeAbstractTypes
&& p.IsInterface == includeInterfaceTypes
&& p.GetInterfaces().Any(i=>i.FullName != null && i.FullName.Contains(interfaceType.FullName))
);
//var implementingTypes = from type in allTypes
// from intf in type.GetInterfaces().ToList()
// where intf.FullName != null && intf.FullName.Contains(interfaceType.FullName)
// select type;
//return implementingTypes.ToArray<Type>();
return allTypes.ToArray();
}
Могу я предложить другое решение?
public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool includeAbstractTypes, bool includeInterfaceTypes)
{
// Use linq to find types that implement the supplied interface.
var implementingTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => interfaceType.IsAssignableFrom(p)
&& (
(p.IsAbstract && includeAbstractTypes)
|| (p.IsInterface && includeInterfaceTypes)
|| (!p.IsAbstract && !p.IsInterface)
)
);
return implementingTypes.ToArray<Type>();
}
SelectMany переводится как секунда "от":
var implementors = from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where type.IsAbstract == includeAbstractTypes
where type.IsInterface == includeInterfaceTypes
from intf in type.GetInterfaces()
where intf.FullName != null &&
intf.FullName.Contains(interfaceType.FullName)
select type;
Я разделил условия на несколько пунктов «где» для субъективной ясности, кстати.
Это компилируется, но я не проверял, работает ли это на самом деле или нет :) Как показал другой ответ, вы можете использовать «Any» с GetInterfaces () вместо последнего предложения «from».
Обратите внимание, что нет необходимости постоянно вызывать ToList () повсюду - LINQ предназначен для работы с последовательностями повсюду.
Кстати, я не уверен, почему вы используете type.GetInterfaces () для проверки. Есть ли что-то отличное (и желаемое) от этого по сравнению с использованием Тип. IsAssignableFrom? Так было бы проще:
var implementors = from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where type.IsAbstract == includeAbstractTypes
where type.IsInterface == includeInterfaceTypes
where interfaceType.IsAssignableFrom(type)
select type;
У вас действительно есть такое же имя типа интерфейса в другой сборке?
Изначально у меня был IsAssignableFrom, но я обнаружил, что когда вы предоставляете общий интерфейс без определенного типа, например. typeof (IInterface <>), он не работает, поскольку фактические классы реализуют определенные типы, например ConcreteClass: IInterface <string>, поэтому они не совпадают.
@Davide: Тогда сравнение по имени тоже не сработает. Если вы так думаете, приведите полный пример :)
@Jon: Я обновил вопрос примером (надеюсь, это имеет смысл), дайте мне знать, если вы думаете, что мне что-то не хватает при сравнении FullName. Честно говоря, это не мой предпочтительный метод, но я пока не уверен в альтернативе.
Ах, я пропустил, что вы используете FullName.Contains, а не FullName ==. Лично я бы написал отдельный метод, чтобы сделать это «правильно» (используя GetGenericTypeDefinition), но если вас устраивает FullName, первый запрос выше должен работать.
Джон, я опубликовал свой собственный ответ с окончательным решением, я нашел свою альтернативу (ура) и решил использовать GetGenericTypeDefinition, как обсуждалось. Спасибо за обсуждение, это помогло.
После короткого обсуждения с Джоном Скитом и еще немного размышлений я опубликовал следующий ответ. Я изменил метод, чтобы использовать GetGenericTypeDefinition вместо FullName.Contains, это было бы более надежным решением. Я также изменил предложения Where в LINQ-запросе для IsAbstract и IsInterface, поскольку они не исключают типы, как ожидалось. Спасибо всем за ваш отзыв.
/// <summary>
/// Returns an array of Types that implement the supplied generic interface in the
/// current AppDomain.
/// </summary>
/// <param name = "interfaceType">Type of generic interface implemented</param>
/// <param name = "excludeAbstractTypes">Exclude Abstract class types in the search</param>
/// <param name = "excludeInterfaceTypes">Exclude Interface class types in the search</param>
/// <returns>Array of Types that implement the supplied generic interface</returns>
/// <remarks>
/// History.<br/>
/// 11/12/2008 davide Created method.<br/>
/// 11/12/2008 davide Altered method to use a two LINQ query pass.<br/>
/// 11/12/2008 davide Altered method to use optimised combined LINQ query.<br/>
/// 12/12/2008 davide Altered method and replaced FullName criteria match with GetGenericTypeDefinition.<br/>
/// </remarks>
public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool excludeAbstractTypes, bool excludeInterfaceTypes)
{
if (!interfaceType.IsGenericType)
{
throw new ArgumentException("Supplied interface is not a Generic type");
}
if (interfaceType.ContainsGenericParameters)
{
interfaceType = interfaceType.GetGenericTypeDefinition();
}
// Use linq to find types that implement the supplied generic interface.
var implementingTypes = from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where (type.IsAbstract != excludeAbstractTypes) || (!excludeAbstractTypes)
where (type.IsInterface != excludeInterfaceTypes) || (!excludeInterfaceTypes)
from intf in type.GetInterfaces()
where intf.IsGenericType && intf.GetGenericTypeDefinition() == interfaceType
select type;
return implementingTypes.ToArray<Type>();
}
Спасибо, Бруно, ознакомьтесь с комментарием, который я сделал к сообщению Джона Скита об IsAssignableFrom, он может вас заинтересовать.