h.alex
h.alex

Reputation: 902

C# generics is keyword covariance / contravariance type inferral

Please consider this interface:

public interface IInitialiazableEntity<TRepository> where TRepository : class, IRepository
{
    void Initialize(TRepository repository);
}

This class (snippet):

public class SomeFacade<TRepository> where TRepository : class, IRepository
{
        public void Add<TEntity>(TEntity entity) where TEntity : AbstractEntity
        {
            try
            {
                if (entity is IInitialiazableEntity<TRepository>)
                    (entity as IInitialiazableEntity<TRepository>).Initialize(this.Repository);
            }
            catch (Exception ex)
            {
                this.Logger.Error(ex);
            }
        }
}

And the entity:

public class Student : AbstractEntity, IInitialiazableEntity<IRepository>
{ 
    void Initialize(IRepository repository) { ... }
}

Since the student is only IInitialiazableEntity<IRepository> and the facade will have an actual repository which is more specialized than the basic IRepository (i.e. it will be IMySpecialRepository : IRepository), will the is keyword realize that it can cast the IMySpecialRepository and pass it to the Initialize method of the entity? Or if not, how to do it?

Upvotes: 0

Views: 262

Answers (2)

Lukazoid
Lukazoid

Reputation: 19426

Currently, if you had an instance SomeFacade<IMySpecialRepository> the code will not work.

You have two options, one is the answer provided by Dan Bryant, the other is to make IInitialiazableEntity<TRepository> contravariant.

Your interface declaration will become (note the in on the generic type):

public interface IInitialiazableEntity<in TRepository> where TRepository : class, IRepository
{
    void Initialize(TRepository repository);
}

This will allow your is check and cast to work, as IInitialiazableEntity<IRepository> can be cast to IInitialiazableEntity<IMySpecialRepository> when using a contravariant generic.

See here for more information about Covariance and Contravariance in generics.

Upvotes: 1

Dan Bryant
Dan Bryant

Reputation: 27505

Assuming your entity is of type Student, the 'is' will return false; this is because Student doesn't implement the more specific interface specialization. You don't need to cast to this more specific interface, though. This should work just fine:

public class SomeFacade<TRepository> where TRepository : class, IRepository
{
        public void Add<TEntity>(TEntity entity) where TEntity : AbstractEntity
        {
            try
            {
                if (entity is IInitialiazableEntity<IRepository>)
                    (entity as IInitialiazableEntity<IRepository>).Initialize(this.Repository);
            }
            catch (Exception ex)
            {
                this.Logger.Error(ex);
            }
        }
}

Upvotes: 1

Related Questions