getting type T from IEnumerable T
IEnumerable<T> myEnumerable;
Type type = myEnumerable.GetType().GetGenericArguments()[0];
Thusly,
IEnumerable<string> strings = new List<string>();
Console.WriteLine(strings.GetType().GetGenericArguments()[0]);
prints System.String
.
See MSDN for Type.GetGenericArguments
.
Edit: I believe this will address the concerns in the comments:
// returns an enumeration of T where o : IEnumerable<T>
public IEnumerable<Type> GetGenericIEnumerables(object o) {
return o.GetType()
.GetInterfaces()
.Where(t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.Select(t => t.GetGenericArguments()[0]);
}
Some objects implement more than one generic IEnumerable
so it is necessary to return an enumeration of them.
Edit: Although, I have to say, it's a terrible idea for a class to implement IEnumerable<T>
for more than one T
.
Get type of T in IEnumerable T
If T
is List<T>
// add error checking to taste
var typeOfEnumerable = typeof(T).GetInterfaces().Where(i => i.IsGenericType)
.Single(i => i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.GetGenericArguments().First();
See it in action.
If T
is IEnumerable<T>
// add error checking to taste
var typeOfEnumerable = typeof(T).GetGenericArguments().First();
Important notes
You really should think about what MyClass
is trying to do here. Why does it want to special-case for T
being an IEnumerable<X>
? Perhaps some constraints should be added on T
? It's not possible to answer these and other important questions without more concrete information, but you really should do so.
Also, did you name the method GetType
by accident or on purpose? It's really, really, really not a good idea to hide object.GetType
like that, so I would suggest you rename that method.
How to get a base type of a IEnumerable
You can do this if you want the type of PossibleValues:
var type = PossibleValues.GetType().ToString(); // "System.Nullable`1[System.Int32][]"
Or you can do this if you want the type of an item contained in PossibleValues (assuming the array actually has values as described in your question):
var type = PossibleValues.Cast<object>().First().GetType().ToString(); // "System.Int32"
EDIT
If it's a possibility that the array may contain no items, then you'll have to do some null checking, of course:
var firstItem = PossibleValues.Cast<object>().FirstOrDefault(o => o != null);
var type = string.Empty;
if (firstItem != null)
{
type = firstItem.GetType().ToString();
}
Checking if Type or instance implements IEnumerable regardless of Type T
The following line
return (type is IEnumerable);
is asking "if an instance of Type
, type
is IEnumerable
", which clearly it is not.
You want to do is:
return typeof(IEnumerable).IsAssignableFrom(type);
How to check whether a Generic Type T is IEnumerable T' where T' is unkown
Because you are only calling .ToString()
you actually don't really care what T is, only if it implements IEnumerable
or not. Here is how to do it without reflection and just using IEnumerable
instead of IEnumerable<T>
, I do my own logic for String.Join
because it made it easier to code the recursive logic.
internal static class ExtensionMethods
{
public static String Join<T>(this IEnumerable<T> enumerable)
{
StringBuilder sb = new StringBuilder();
JoinInternal(enumerable, sb, true);
return sb.ToString();
}
private static bool JoinInternal(IEnumerable enumerable, StringBuilder sb, bool first)
{
foreach (var item in enumerable)
{
var castItem = item as IEnumerable;
if (castItem != null)
{
first = JoinInternal(castItem, sb, first);
}
else
{
if (!first)
{
sb.Append(",");
}
else
{
first = false;
}
sb.Append(item);
}
}
return first;
}
}
Here is a test program I wrote that shows it all works (it tests classes, structs, and IEnumerables 3 layers deep).
EDIT: Per your comment here is another version that flattens out the nested IEnumerables, you can do whatever you want to each element when you are done.
internal static class ExtensionMethods
{
public static IEnumerable<T> SelectManyRecusive<T>(this IEnumerable enumerable)
{
foreach (var item in enumerable)
{
var castEnumerable = item as IEnumerable;
if (castEnumerable != null
&& ((typeof(T) != typeof(string)) || !(castEnumerable is string))) //Don't split string to char if string is our target
{
foreach (var inner in SelectManyRecusive<T>(castEnumerable))
{
yield return inner;
}
}
else
{
if (item is T)
{
yield return (T)item;
}
}
}
}
}
There also was a bug I ran in to that I think may afffect my first part of my answer, a string
is technically a IEnumerable<char>
so a IEnumerable<string>
could be also seen as a IEnumerable<IEnumerable<char>>
and it may put too many ,
in. This second version has a check for that.
Test program showing how to use this method and String.Join
together.
Why can't I replace IEnumerable T by a generic type variable in extension method?
Why?
For exactly the reason stated in the error message: you're trying to use IEnumerable<Customers>
as the type argument for E
, but E
has this constraint:
where E : System.Linq.IQueryable<T>
And how can it be fixed?
It can't, assuming I understand what you're trying to achieve.
There's a fundamental problem with the "simplification" you're trying to achieve: you don't actually have full duplication in your original MySelect1
methods. The first calls AsEnumerable()
and the second calls AsQueryable()
. You're trying to replace those with a cast, and that's just not going to work.
There's a further problem, even with your original methods: you're accepting Func<T, V> f
as a parameter for your queryable-based method, which means any time you call Select
or similar and passing in f
, you'll be calling Enumerable.Select
instead of Queryable.Select
. To really use IQueryable<>
properly, you should accept Expression<Func<T, V>> f
instead. At that point, you won't need to call AsQueryable
anyway.
Your two methods "should" take radically different paths based on whether you're using LINQ to Objects or a different LINQ provider (e.g. LINQ to SQL), and that can't be hidden as a pure implementation detail without significant changes that would probably make it less useful than you want anyway.
Create a Generic IEnumerable T given a IEnumerable and the member datatypes
You can use reflection to call Cast<> with a runtime type. See this answer: How to call a generic method through reflection
However, at some point you'll still have to know the type of the item at compile time if you ever want to manipulate it as such without reflection. Just having an IEnumerable<T>
instance cast as an object/IEnumerable still won't really do you any more good than just having the IEnumerable by itself. It's likely that you need to rethink what you're doing.
Related Topics
How to Make Multi-Language App in Winforms
List<T> Orderby Alphabetical Order
How to Inject a Dbcontext Instance into an Ihostedservice
How to Download a File from a Website in C#
Most Elegant Xml Serialization of Color Structure
C#: How to Make It Harder for Hacker/Cracker to Get Around or Bypass the Licensing Check
Web API Put Request Generates an Http 405 Method Not Allowed Error
Managing Multiple Selections with Mvvm
Extracting Mantissa and Exponent from Double in C#
Nullable Type Is Not a Nullable Type
Synchronously Waiting for an Async Operation, and Why Does Wait() Freeze the Program Here
Will Code in a Finally Statement Fire If I Return a Value in a Try Block
How to Convert String to Double with Proper Cultureinfo
How to Check If a String Exists in Another String