Here's the situation: I'm attempting to get a collection of all types in my assembly that implement a specific generic interface along with the generic type parameters used. I have managed to put together a Linq query to perform this but it seems awfully redunant.
I've read up on let and joins but couldn't see how to I'd use them to reduce the verbosity of this particular query. Can anyone provide any tips on how to shorten/enhance the query please?
Here's an MSTest class that currently passes and demonstrates what I'm trying to achieve:
[TestClass]
public class Sample
{
[TestMethod]
public void MyTest()
{
var results =
(from type in Assembly.GetExecutingAssembly().GetTypes()
where type.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(MyInterface<,>)
)
select new ResultObj(type,
type.GetInterfaces().First(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(MyInterface<,>)
).GetGenericArguments()[0],
type.GetInterfaces().First(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(MyInterface<,>)
).GetGenericArguments()[1]
)).ToList();
Assert.AreEqual(1, results.Count);
Assert.AreEqual(typeof(int), results[0].ArgA);
Assert.AreEqual(typeof(string), results[0].ArgB);
}
interface MyInterface<Ta, Tb>
{ }
class MyClassA : MyInterface<int, string>
{ }
class ResultObj
{
public Type Type { get; set; }
public Type ArgA { get; set; }
public Type ArgB { get; set; }
public ResultObj(Type type, Type argA, Type argB)
{
开发者_StackOverflow Type = type;
ArgA = argA;
ArgB = argB;
}
}
}
Regards,
Matt
Here is an example that shows how to rewrite this using the let
keyword:
var results =
(from type in Assembly.GetExecutingAssembly().GetTypes()
// Try to find first such interface and assign the result to 'ifc'
// Note: we use 'FirstOrDefault', so if it is not found, 'ifc' will be null
let ifc = type.GetInterfaces().FirstOrDefault(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(MyInterface<,>))
// Filtering and projection can now use 'ifc' that we already have
where ifc != null
// Similarly to avoid multiple calls to 'GetGenericArguments'
let args = ifc.GetGenericArguments()
select new ResultObj(type, args[0], args[1])).ToList();
The let
keyword works a bit like variable declaration, but lives within LINQ queries - it allows you to create a variable that stores some result that is needed in multiple places later in the query. You mention "joins" as well, but that's mostly used for database-like joins (and I'm not sure how it would apply here).
精彩评论