How to Do Generic Polymorphism on Open Types in C#

How to do generic polymorphism on open types in C#?

Since you don't want to force a specific type, make an abstract class or an interface this way:

interface IFoo { }

And inherit it in your type-specific Foo (with generic parameters), like:

interface IFoo<T> : IFoo {}

This way you can just have a List<IFoo> and add IFoo<T> instead.

More examples:

class FooCollection {
private List<IFoo> _collection;

public FooCollection()
{
_collection = new List<IFoo>();
}

public void Add(IFoo foo)
{
_collection.Add(foo);
}

public void Remove(IFoo foo)
{
_collection.Remove(foo);
}
}

Polymorphism in C# generic types

You can't define class Concrete<int> with <int> - it's like you're trying to override the normal definition of int with a new generic type called int. But then in the class you're trying to actually return an int.

So it should look like:

class Concrete : IOpen<int>
{
public int A => 42;
public string B => "42";
}

But now the class WorkerInt would have to look like this:

class WorkerInt : IWorker<int>
{
public void Do(Concrete item)
{
Console.WriteLine(item.A);
Console.WriteLine(item.B);
}
}

But IWorker<int> must implement void Do(IOpen<T> item) and even though Concrete implements IOpen<T> you can't use void Do(Concrete item) because it is more restrictive than void Do(IOpen<T> item).

So you must define it this way:

class WorkerInt : IWorker<int>
{
public void Do(IOpen<int> item)
{
Console.WriteLine(item.A);
//Console.WriteLine(item.B);
}
}

But that makes item.B no longer work as IOpen<int> doesn't have a B property.

The only way to make this work is to change IWorker to be this:

interface IWorker<T, R> where T : IOpen<R>
{
void Do(T item);
}

Then WorkerInt can be this:

class WorkerInt : IWorker<Concrete, int>
{
public void Do(Concrete item)
{
Console.WriteLine(item.A);
Console.WriteLine(item.B);
}
}

Realizing polymorphism with Generics in C#

It is possible to achieve this with generics as such:

First, you want to create Generic classes for your parser and repository:

         public class Parser<T> : IParser<T>
{
IList<T> ParseDataTableToList(DataTable dataTable, object o)
{
var list = new List<T>();
//Your parsing logic can go:
//1) Here(using reflection, for example) or
//2) in the constructor for Motor/Switchboard object,
//in witch case they will take a reader or row object
return list;
}
}
public class Repo<T> : IInsertOrUpdateList<T>
{
void InsertOrUpdate(IList<T> list)
{
//...
}
}

And then, your Generic window class looks like this:

public partial class ImportView<T> : Form
{
private IParser<T> parser;
private IInsertOrUpdateList<T> repository;
private DataTable dataToParse;

public ImportView(DataTable dataToParse)
{
this.parser = new Parser<T>();
this.repository = new Repo<T>();
this.dataToParse = dataToParse;
}

public void ParseAndInsertIntoDB()
{
repository.InsertOrUpdate(parser.ParseDataTableToList(dataToParse, null));
}
}

And you instantiate it like this:

var ImportView = new ImportView<Motor>(YourDataTable)

How to create List of open generic type of classT?

There is no diamond operator in C# yet, so you can't use true polymorphism on open generic type underlying to closed constructed types.

So you can't create a list like this:

List<Data<>> list = new List<Data<>>();

You can't use polymorphism on such list... and it is a lack in genericity here.

For example, in C# you can't create a List<Washer<>> instance to have some Washer<Cat> and some Washer<Dog> to operate Wash() on them...

All you can do is using a list of objects or an ugly non generic interface pattern:

public interface IData
{
void SomeMethod();
}

public abstract class Data<T> : IData
{
public void SomeMethod()
{
}
}

List<IData> list = new List<IData>();

foreach (var item in list)
item.SomeMethod();

You can also use a non generic abstract class instead of an interface:

public abstract class DataBase
{
public abstract void SomeMethod();
}

public abstract class Data<T> : DataBase
{
public override void SomeMethod()
{
}
}

List<DataBase> list = new List<DataBase>();

foreach (var item in list)
item.SomeMethod();

But you lost some genericity design and strong-typing...

And you may provide any non-generic behavior such as properties and methods you need to operate on.


Generics open and closed constructed types

C# variance problem: Assigning List<Derived> as List<Base>

How to do generic polymorphism on open types in C#?

C# : Is Variance (Covariance / Contravariance) another word for Polymorphism?

C# generic inheritance and covariance part 2

still confused about covariance and contravariance & in/out


Covariance and Contravariance (C#)

Covariance & Contravariance

Generic Classes (C# Programming Guide)

Polymorphism by generic types where types do not share a base class

If the entities do not have the same base class, the best you can do is to have a class constraint.

Since both expressions are essentially the same, you should just pass one expression and a function to get the key value from the entity.

The Count and First methods can also be merged into a single statement and then checking for null.

int? MethodToRefactor<T>(DbSet<T> entities, Func<string, Expression<Func<T, bool>>> expressionFilter, Func<T, int> getIdFunc, IList<string> someCollection, string[] moreParams)
where T : class
{
int? keyValue = null;
foreach (var itemDetail in someCollection)
{
string refText = GetRefTextBySource(itemDetail, moreParams);
var entity = entities.FirstOrDefault(expressionFilter(refText));
if (entity != null)
{
keyValue = getIdFunc(entity);
}
if (...some conditional code...)
break;
}
return keyValue;
}

You would call the method like this

id = MethodToRefactor<Account>(db.Accounts, txt => a => a.Name == txt, a => a.AccountId, ...);
id = MethodToRefactor<Customer>(db.Customers, txt => c => c.CustomerName == txt, c => c.CustomerId, ...);

C# Generics and polymorphism: an oxymoron?

As far as I can see, consuming code doesn't need specifics of generic class (i.e., it doesn't depends on what T is). So, why don't you introduce interface that SomeClass<T> will implement, and use instance of this interface.

E.g.:

public interface ISome
{
void SomeMethod();
}

public class SomeClass<T>: ISome
{
public virtual void SomeMethod(){ }
}

public void DoSomethingClienty()
{
Factory factory = new Factory();
ISome someInstance = factory.Create();

someInstance.SomeMethod();
}

Now, subclasses of SomeClass<T> can operate differently on different Ts, but consuming code won't change.

How to use an object of a generic class inside a generic class in C#?

It's actually impossible because not supported by the compiler else it would cause a stack overflow of the compiler with such thing that is endless and so impossible, because of the generic type verification as currently defined by the .NET specifications that are more strict than templates in C++.

You could use a LinkedList<ChainedItem<>> but the diamond operator hence true generic polymorphism are not available in C#:

How to do generic polymorphism on open types in C#?

Therefore perhaps you can write such thing using an interface and type checking, excluding null values:

public interface IChainedItem
{
void DoOperation(object value);
}
public abstract class ChainedItem<T> : IChainedItem
{
private IChainedItem m_chainedItem;

public void SetNextChainedItem(IChainedItem chainedItem)
{
m_chainedItem = chainedItem;
}

public abstract void DoOperation(T data);

public virtual void ContinueNextOperation(object data)
{
if ( m_chainedItem != null )
m_chainedItem.DoOperation(data);
}

public void DoOperation(object value)
{
if ( value == null )
throw new ArgumentNullException();
var typeIn = value.GetType();
var typeRequired = typeof(T);
if ( typeIn != typeRequired )
{
string message = $"Bad type in ChainedItem.DoOperation(object): "
+ $"{typeRequired.Name} is required and not {typeIn.Name}.";
throw new ArgumentException(message);
}
DoOperation((T)value);
}
}

Type.GetMethod() for polymorphic method (both generic and non-generic)

In .NET 4.7.1 you need to use method GetMethods and filter the results:

class MyClass
{
public T M<T>(T t) { return default(T); }
public int M(int t) { return 0; }
}

var m = typeof(MyClass).GetMethod("M", new[] { typeof(string) }); // null
var m1 = typeof(MyClass).GetMethods()
.Where(mi => mi.Name == "M" && mi.GetGenericArguments().Any())
.First(); // returns generic method

In .NET Standard 2.1 (and .NET Core since 2.1) there is another way to resolve generic type arguments - Type.MakeGenericMethodParameter, like you can see it in this answer.

Also as workaround you can move your copyAutofields<T> method to generic class like CopyAutoFieldServer<T>:

public static class CopyAutoFieldServer<T>
{
public static T copyAutofields(T original) { ... }
}

How to define an aggregated ICollectionT where T is type of the current declaring class within a hierarchy?

No, there is no way to do such thing yet.

  • The C# language has no artifact to declare such thing:

    class A
    {
    public ICollection<T> Children where T : thisdeclaring;
    }

    Where thisdeclaring represents the current declaring type.

  • C# does not support true polymorphism on open types using the diamond operator <>

    How to create List of open generic type of class?

    How to do generic polymorphism on open types in C#?

Solution 1 : type checking hack on the non generic type

We check the type at runtime to throw an exeption in case of mismatch, but we must lost the genericity as explained in previous links:

using System.Reflexion;

class A
{
private ICollection _Children;
public ICollection Children
{
get => _Children;
set
{
if ( value == null )
{
_Children = null;
return;
}
var genargs = value.GetType().GenericTypeArguments;
if (genargs.Length != 1 || this.GetType() != genargs[0] )
{
string msg = $"Type of new {nameof(Children)} items must be {this.GetType().Name}: "
+ $"{ genargs[0].Name} provided.";
throw new TypeAccessException(msg);
}
_Children = value;
}
}
}

Test

var a = new A();
trycatch(() => a.Children = new List<A>());
trycatch(() => a.Children = new List<B>());
Console.WriteLine();
var b = new B();
trycatch(() => b.Children = new List<A>());
trycatch(() => b.Children = new List<B>());
void trycatch(Action action)
{
try
{
action();
Console.WriteLine("ok");
}
catch ( Exception ex )
{
Console.WriteLine(ex.Message);
}
}

Output

ok
Type of new Children items must be A: B provided.

Type of new Children items must be B: A provided.
ok

So we can't have the generic type parameter on the collection and the hierarchical type contrainst at the same time, as I know, for the moment.

Solution 2 : the same hack using dynamic to keep genericity

private dynamic _Children;
public dynamic Children
  set
{
if ( value == null )
{
_Children = null;
return;
}
bool isValid = false;
foreach ( Type type in value.GetType().GetInterfaces() )
if ( type.IsGenericType )
if ( type.GetGenericTypeDefinition() == typeof(ICollection<>) )
{
isValid = true;
break;
}
if ( !isValid )
{
string msg = $"{nameof(Children)} must be a ICollection of {this.GetType().Name}: "
+ $"{value.GetType().Name} provided.";
throw new TypeAccessException(msg);
}
var genargs = value.GetType().GenericTypeArguments;
if ( genargs.Length != 1 || this.GetType() != genargs[0] )
{
string msg = $"Type of new {nameof(Children)} items must be {this.GetType().Name}: "
+ $"{ genargs[0].Name} provided.";
throw new TypeAccessException(msg);
}
_Children = value;
}
}

Here we keep the generic closed constructed type of the collection.

Thus we can use all generic members of the stored instance.



Related Topics



Leave a reply



Submit