Infer Generic Type of Interface - c#

-- Context
I have the following 5 objects
IChangeStatus<T>
myFirstClass : IChangeStatus<firstClassEnum>
mySecondClass : IChangeStatus<secondClassEnum>
myClassHandler<TEntity>
myFirstClassHandler : myClassHandler<myFirstClass>
for the purposes of the question we can assume the interface only has the property
T Status { get; }
-- Questions
1.- How can I ask in a method in myClassHandler if an instance of TEntity implements IChangeStatus?
2.- How can I iterate over an IEnumerable of TEntity assuming their specific IChangeStatus?

To check if your class implements IChangeStatus, you can simply do:
public void FooMethod(ClassType myClass)
{
var doesImplementIChange = myClass as IChangeStatus<SomeClass>
if (doesImplementIChange != null)
{
// Do stuff..
}
}
To iterate over an IEnumerable of your classes:
foreach (var data in myClass.OfType<MyType>())
{
// Do stuff..
}
or, you could do:
foreach (var cls in myClass)
{
var myCls = myClass as IChangeStatus<SomeClass>;
if (myCls != null)
{
// Do stuff..
}
}

If you want to use T from IChangeStatus<T> in MyClassHandler, you will have to add another type parameter. For example:
class MyClassHandler<TEntity, TStatus>
where TEntity : IChangeStatus<TStatus>
{
public IEnumerable<TStatus> Statuses
{
get { return _entities.Select(entity => entity.Status); }
}
}
The where clause will ensure that the entity and status types are correlated.
If you don't want to do that, you could add an additional non-generic interface that exposes a Status property of the type Object. You'd lose some static typing that way, but you would not need the extra type parameter.

I found this other SO Question - Check if a type implements a generic interface without considering the generic type arguments which gave me a more generic answer which is what I was looking for:
return entity.GetType().GetInterfaces()
.Where(i => i.IsGenericType)
.Any(i => i.GetGenericTypeDefinition() == typeof(IChangeStatus<>));
As to the iteration over the IEnumerable assuming the specific type of IChangeStatus, since we got that to point then the type does implement the interface thus has a Status property... so I went for dynamic type.

Related

Check if a Type derives from a Interface with more than one Generic Argument

I have a question about loading types with reflection. I am attempting to filter the list of types in an Assembly by those which implement an interface with two generic parameters. I don't intend to explicitly tell which types are those generic parameters since I want all classes that implement the interface but whenever I attempt to use typeof(IExample<>) it gets marked as an error. However, it's possible to do it with an interface that only has one generic parameter.
I would really appreciate some help with this! Thanks in advance :)
public interface IExample<T, E>
{
}
This is how my interface would looks like.
And then I currently have to classes that implement it.
public class C
{
}
public class A : IExample<string, C>
{
}
Public class B : IExample<XMLDocument, C>
{
}
You weren't to far off from what I could examine from your question. In order to get the correct generic type, without any generic arguments you need to call typeof(IExample<,>), note that there is a comma!
For the other part of your question on how to get those types you can do something like the following:
public static IEnumerable<Type> GetTypesWithGenericArguments(Assembly assembly, Type implementingInterface)
{
var types = assembly.GetTypes();
// Loop over all Types in the assembly
foreach (var type in types)
{
// Skipping all Types which aren't classes
if (!type.IsClass)
continue;
var implementedInterfaces = type.GetInterfaces();
// Loop over all interfaces the type implements
foreach (var implementedInterface in implementedInterfaces)
{
// Return the type if it one of its interfaces are matching the implementingInterface
if (implementedInterface.IsGenericType && implementedInterface.GetGenericTypeDefinition() == implementingInterface)
{
yield return type;
// You can leave the loop, since you don't need to check the other
// interfaces, since you already found the one you were searching for.
break;
}
}
}
}
Which could be used like that:
public static void Main(string[] args)
{
foreach (var item in GetTypesWithGenericArguments(Assembly.GetCallingAssembly(), typeof(IExample<,>)))
{
Console.WriteLine(item.Name);
}
// This would print to the Console:
// A
// B
}

Checking Whether Class Implements Generic Interface

I'm using the following generic function to determine whether a class implements a specified interface:
private static bool HasFieldType<TEntity, TInterface>()
{
return typeof(TInterface).IsAssignableFrom(typeof(TEntity));
}
This works fine for the majority of the time.
However, I now have an interface which has a generic parameter:
public interface IStatusField<TEnum> where TEnum : System.Enum
{
TEnum Status { get; set; }
}
And this causes the HasFieldType function to break with an unexpected use of unbound generic name error.
Ideally, I want to call the function like:
if (HasFieldType<TEntity, IStatusField<>>())
{
// builder is an EntityTypeBuilder instance
builder.Property("Status")
.HasMaxLength(255)
.HasConversion(new EnumToStringConverter<>());
}
But this won't work as I'm not specifying the generic type for both the IStatusField<> or the EnumToStringConverter<>.
Is there any way around this?
UPDATE
This code forms part of a generic base IEntityTypeConfiguration class as follows:
public abstract class EntityTypeConfiguration<TPrimaryKey, TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : Entity<TPrimaryKey>
{
public void Configure(EntityTypeBuilder<TEntity> builder)
{
builder.HasKey(e => e.Id);
builder.Property(e => e.Id)
.IsRequired()
.HasMaxLength(13)
.HasValueGenerator<PrimaryKeyValueGenerator>();
// Apply the generic interface properties
builder.ApplyInterfaceFields<TPrimaryKey, TEntity>();
// Apply any additional configuration
this.OnConfigure(builder);
}
protected abstract void OnConfigure(EntityTypeBuilder<TEntity> builder);
}
// In an extension class, I have
public static void ApplyInterfaceFields<TPrimaryKey, TEntity>(this EntityTypeBuilder<TEntity> builder) where TEntity : Entity<TPrimaryKey>
{
// Check other implementations (removed for brevity)
// IStatusField implementation
if (HasFieldType<TEntity, IStatusField<>())
{
builder.Property("Status")
.HasMaxLength(255)
.HasConversion(new EnumToStringConverter<>());
}
}
At the point of checking for IStatusField implementation, I know nothing about the generic type specified. I think this may be the bigger problem...
Ok, so I've managed to punch my way around the problem.
It needs a bit of tidying and some error checking but in the crudest form:
private static bool HasFieldType<TEntity>(Type interfaceType)
{
var interfaces = typeof(TEntity).GetTypeInfo().ImplementedInterfaces;
// Iterate through the interfaces
foreach (var intf in interfaces)
{
// Compare interface names
if (intf.FullName.Contains(interfaceType.FullName))
{
return intf.IsAssignableFrom(typeof(TEntity));
}
}
return false;
}
Which now enables this to work:
// IStatusField implementation
if (HasFieldType<TEntity>(typeof(IStatusField<>)))
{
builder.Property("Status")
.HasMaxLength(255)
.HasConversion<string>();
}
I can just use the built-in automatic string-to-enum conversions from EF to do the grunt work.
Instead of trying to wrestle with resolving generic type arguments from nothing, you might consider approaching it from the opposite direction, by getting a list of interfaces implemented by TEntity, filtering it to search for an IStatusField. Once you've located the field, you can get its' generic type arguments and pass those to your EnumToStringConverter:
var statusField = typeof(TEntity)
.GetInterfaces()
.FirstOrDefault(x => x.Name.StartsWith("IStatusField"));
Value given TEntity : IStatusField<ConsoleColor>:
statusField.GenericTypeArguments = [ typeof(System.Color) ]
From there though you're not done; you must still construct an instance of the generic type EnumToStringConverter<System.Color>. This is rather simple and outlined here.
Edit: I realized that because you'd be invoking a constructor, it's not quite the same. Here's how you'd accomplish this:
var statusField = typeof(TEntity)
.GetInterfaces()
.FirstOrDefault(x => x.Name.StartsWith("IStatusField"));
if (statusField != null)
{
var enumType = statusField.GenericTypeArguments[0]; // get the IStatusField<T> value
// get the default constructor after supplying generic type arg
var converterType = typeof(EnumToStringConverter<>)
.MakeGenericType(enumType)
.GetConstructors()[0];
// invoke the constructor. Note the null (optional) param
dynamic converter = converterType.Invoke(new Object[1]);
builder.Property("Status")
.HasMaxLength(255)
.HasConversion(converter);
}

Implementing generic methods from an interface using another interface

I'm attempting to create a common interface which will allow me n methods of interacting with a database. I want my business application to be able to instantiate any of the connection methodologies and be assured the interface is identical.
Here's a simplified version of what I'm trying now.
Database Interface where IElement is another interface which would define a table.
public interface IDatabase
{
void setItem( IElement task ); //this works fine
List<T> listTasks<T>() where T : IElement; // this doesn't
}
IElement interface:
public interface IElement
{
int id { get; set; }
}
Implementation of IElement:
public class TaskElement: IElement
{
public int id { get; set; }
public string name {get; set; }
}
Implementation of IDatabase:
public class SQLiteDb: IDatabase
{
public SqLiteDb( SQLiteConnection conn )
{
database = conn;
}
public void setItem( IElement task )
{
// works fine when passed a new TaskElement() which is an implementation of IElement.
database.Insert( task );
}
//it all goes off the rails here
public List<T> listItems<T>() where T : IElement
{
var returnList = new List<IElement>
foreach (var s in database.Table<TaskElement>())
{ returnList.Add(s); }
return returnList;
}
I've tried a lot of variations on this but each one gives me a new issue. Here, for instance, there are two errors.
1)
The type arguments for method 'SQLiteDb.listTasks<T>()' cannot be inferred from the usage. Try specifying the type arguments explicitly.
2)
Cannot implicitly convert type 'System.Collections.Generic.List<TaskElement>' to 'System.Collections.Generic.List<T>'
I've tried changing the method to use an explicit type but have had issues there. If I use IElement (my generic interface for all elements )I can't return a list of TaskElement objects (my implementation of IElement) as it doesn't match the return type (List<IElement>) and if I change the return type to List<TaskElement> I'm not longer implementing the interface.
It's worth noting that I can easily get this to work if I stop using the interface and generics, but this seems like an ideal situation to use an interface. Maybe I'm trying to hard to cram a lot of stuff into an interface when another application (like direct inheritance) might be better?
Question
How can I implement an interface with a generic return value while limiting the types which can be returned to only implementations of another interface.
Let's look closely at your implementation of listItems:
public List<T> listItems<T>() where T : IElement
{
var returnList = new List<IElement>
foreach (var s in database.Table<TaskElement>())
{ returnList.Add(s); }
return returnList;
}
What you've done here is written a method where the caller is allowed to ask for any type they want in the list as long as that type implements IElement. But the code in your method doesn't give them a list of the type they want, it gives them a list of IElement. So it's violating the contract.
But the real root of your problem is database.Table<TaskElement>(). That can only ever give you instances of TaskElement. You need to make that T, but to do that you need an additional generic constraint:
public List<T> listItems<T>() where T : IElement, new
{
var returnList = new List<T>
foreach (var s in database.Table<T>())
{
returnList.Add(s);
}
return returnList;
}
This is because database.Table<T> has a new constraint, which means that it can only be given types that have a zero-parameter constructor (because that method is going to create instances of the given class).
I belive it should be something like this
public List<T> listItems<T>() where T : IElement
{
var returnList = new List<T>
foreach (var s in database.Table<T>())
{ returnList.Add(s); }
return returnList;
}
I think you are on the right track with explicitly defining your list like this:
public interface IDatabase
{
void setItem( IElement task ); //this works fine
List<IElement> listTasks<IElement>();
}
Since you can't directly cast List <TaskElement> to List <IElement> you will have to do a conversion in your listTasks method. There are several methods recommended here: Shorter syntax for casting from a List<X> to a List<Y>?. I think the Linq method is the simplest if you are ok with using Linq:
List<IElement> listOfIElement = listOfTaskElement.Cast<IElement>().ToList()
You need to use the Generic type when creating the object instance:
Instead of
var returnList = new List<IElement>();
Do this
var returnList = new List<T>();

Compare without generics

I have a bit complicated issue to solve.
I have a list of objects though later the list will be filled with two different type of instances.
First type is MyFirstType<T1, T2> and second type is MySecondType<T>
Now I need to run though the list of objects and ask which one of the two is each item. Then I need to do some custom logic on the item.
For example:
foreach(object obj in list)
{
if(obj is MyFirstType<T1, T2>)
{
// do something
}
else if(obj is MySecondType<...>)
{
}
}
The problem is T or T1 and T2 could be any types so how do I write such an if - Is Keyword statement that only comparies if MyFirstType but not the generics inside? if(obj is MyFirstType<T1, T2>) does not work since it needs concrete types for T1 and T2.
Like mentioned just need comparison without T1 and T2. Any ideas how to solve this?
You can use IsGenericType property of Type class to check if obj.GetType() is generic, and then use GetGenericTypeDefinition to get generic type definition, which can be compared to typeof(MyFirstType<,>) and typeof(MySecondType<>):
foreach(object obj in list)
{
if(obj.GetType().IsGenericType)
{
if(obj.GetType().GetGenericTypeDefinition() == typeof(MyFirstType<,>))
{
// do something
}
else if(obj.GetGenericTypeDefinition() == typeof(MySecondType<>))
{
}
}
}
The cheap solution: create a flag interface which you derive in your types. This is just a helper to determine the type.
Use reflection and check the type definition and check the name of the type.
Why don't you use polymorphism? Have the two types implement an interface with a common method. Then in the implementation of each type write the code for the method you need.
public interface ICommon
{
void DoThis();
}
For MyFirstType and MySecondType you will have:
public class MyFirstType<T> : ICommon
{
public void DoThis()
{
//DoThis
}
}
public class MySecondType<T1,T2> : ICommon
{
public void DoThis()
{
//DoThis
}
}
Then you could write the following foreach loop:
foreach(object obj in list)
{
obj.DoThis();
}

C# determining generic type

I have several templated objects that all implement the same interface:
I.E.
MyObject<datatype1> obj1;
MyObject<datatype2> obj2;
MyObject<datatype3> obj3;
I want to store these objects in a List... I think I would do that like this:
private List<MyObject<object>> _myList;
I then want to create a function that takes 1 parameter, being a datatype, to see if an object using that datatype exists in my list.... sorta clueless how to go about this. In Pseudo code it would be:
public bool Exist(DataType T)
{
return (does _myList contain a MyObject<T>?);
}
Some Clarification....
My interface is IMyObject<T>, my objects are MyObject<T>. I have a new class MyObjectManager which I need to have a List of MyObject<T> stored within. I need a function to check if a MyObject<T> exists in that list. The type T are datatypes which were auto-generated using T4.... POCO classes from my Entity Data Model.
You can make a generic function:
public bool Exists<T>() where T : class {
return _myList.OfType<MyObject<T>>().Any();
}
Note that this requires that you know T at compile-time.
If all you have is a System.Type object at runtime, you'll need to use reflection:
public bool Exists(Type t) {
var objectOfT = typeof(MyObject<>).MakeGenericType(t);
return _myList.Any(o => o.GetType() == objectOfT);
}
Note, however, that a List<MyObject<object>> cannot hold a MyObject<SomeType>.
You need to change the list to a List<object>, or make MyObject implement or inherit a non-generic type and make the list contain that type.
How about an extension method?
public static bool HasAny(this IEnumerable source, Type type) {
foreach (object item in source)
if (item != null && item.GetType().Equals(type))
return true;
return false;
}
Usage:
bool hasDataType1 = myList.HasAny(typeof(MyObject<datatype1>));
Note that if you don't want to have to type out typeof(...) -- i.e., if you basically want your Exist method to only care about objects of type MyObject<T>, I'd go with something like SLaks's answer:
public static bool Exist<T>(this IEnumerable source) {
return source.OfType<MyObject<T>>().Any();
}
Also, SLaks is right that you really can't have a List<MyObject<object>> that's full of anything other than objects of type MyObject<object> or some derived class (and MyObject<datatype1>, etc. do not derive from MyObject<object> -- generics don't work that way).
Another way I might suggest to work around the whole "you can't get the type of a generic class using a System.Type object without using reflection" issue would be this: Make your MyObject<T> implement a non-generic interface, like this:
public interface IMyObject {
Type DataType { get; }
}
public class MyObject<T> : IMyObject<T>, IMyObject {
public Type DataType {
get { return typeof(T); }
}
}
Then your list could be a List<IMyObject> (the non-generic interface) and your Exist method could look like this:
public static bool Exist<T>(this IEnumerable source, Type type) {
return source.OfType<IMyObject>().Any(x => x.DataType.Equals(type));
}
Since they all implement the same interface, instead of casting them to object and calling GetType (which can be expensive) why not add a property to your interface called class name (or something)? Then you can use the linq in order to grab that property. And don't forget using System.Linq
using System.Linq;
public bool Exist(List<IMyInterface> objects, IMyInterface typeToCheck)
{
return objects.Any(t => t.ObjectName == typeToCheck.ObjectName);
}

Categories