C# covariance: classes with different interface signature - c#

I have a base class with the following signature:
abstract class BaseClass<I, S>
Then I define a class with the following signature to extend the BaseClass:
class ExtendedClass<I> : BaseClass<I, int>
So, ExtendedClass sets the S generic type of the BaseClass with int argument. I am using the classes as it follows:
BaseClass<I, S> anInstance = new ExtendedClass<I>();
This throws the following error:
Cannot implicitly convert type ExtendedClass to BaseClass
Some simplified context:
abstract class Analyzer<I, S>
{
protected BaseClass<I, S> Run()
{
// implements some logic here, based their results different
// extensions of BaseClass can be returned. This logic is
// simplified as the following.
switch(some_logic_output)
{
case true: return new ExtendedClass<I>(){ /*some property settings*/ };
case false: return null;
}
}
}

You should specify for the reference what S and I are. If you are outside the scope of the class:
BaseClass<SomeClass, int> anInstance = new ExtendedClass<SomeClass>();
If you are in the scope of the class
BaseClass<I, int> anInstance = new ExtendedClass<I>();
S is a name given for the generic type in the context of the BaseClass. It does not exist outside of it and when defining a reference as you did you must give the concrete type.

The second type parameter must be set to int:
BaseClass<I, int> anInstance = new ExtendedClass<I>();
Obviously, I also must be a concrete type or another type parameter in a generic class.

Related

How do I correctly constrain to related class type when using a generic method?

I have two base classes BaseObject and BaseObjectSettings. The first defines the object behaviour and the second defines the state of the class (useful for serialisation).
If I want to create a derived BaseObject class with specific settings then I can use a method with a generic type constraint.
public void CreateBaseObjectInstance<T>(BaseObjectSettings baseObjectSettings) where T : BaseObject
{
var instance = pool.GetInstance<T>();
instance.Settings = baseObjectSettings;
scene.Add(instance);
}
The problem I am facing is that while I can constrain the generic type to BaseClass I can't constrain the BaseClassSettings to the relevant derived BaseClass. This means that I can do things like
CreateBaseObjectInstance<Banana>(new AppleSettings());
which seems a bit terrible.
What are my options given that I am currently constrained to both creating and initialising the object in the same method before adding it to the scene?
One way is to have all your settings classes inherit from a generic base class. The generic base class could then inherit from BaseObjectSettings. The generic type parameter indicates what kind of object this settings class is for.
For example, for your AppleSettings,
class AppleSettings: ObjectSettings<Apple> {
...
}
abstract class ObjectSettings<T>: BaseObjectSettings where T: BaseObject {}
Now, you can change CreateBaseObjectInstance to accept an instance of ObjectSettings<T> instead:
public void CreateBaseObjectInstance<T>(ObjectSettings<T> objectSettings) where T : BaseObject
{
var instance = pool.GetInstance<T>();
instance.Settings = objectSettings;
scene.Add(instance);
}
If you pass Banana as T, it would expect ObjectSettings<Banana>, preventing you from giving it AppleSettings, which is ObjectSettings<Apple>.
You need to create a generic interface or base class that where you define the settings type:
public class BaseObject<TSettings>
{
public TSettings Settings { get; set; }
}
Then your method will require two generic arguments - one for the actual object to create TObject and one for method's argument for the settings TSettings. You then constrain TObject to an implementation of the implemented interface or base class/derivation thereof, using generic argument TSettings as the constraint's type's generic argument
public void CreateBaseObjectInstance<TObject, TSettings>(
TSettings settings
)
where TObject : BaseObject<TSettings>
{
...
}
Example (using above BaseObject implementation):
public class MyObjectSettings
{
...
}
public class MyObject : BaseObject<MyObjectSettigns>
{
}
Method call:
var settings = new MyObjectSettings(){ ... };
CreateBaseObjectInstance<MyObject>( settings ); // second generic argument should be inferred
I don't really understand the logic here as things are missing, but from the code provided you can probably write:
public void CreateBaseObjectInstance<TBase, TSettings>(TSettings baseObjectSettings)
where TBase : BaseObject
where TSettings : BaseObjectSettings
Used like that:
CreateBaseObjectInstance<Banana, AppleSettings>(new AppleSettings());
Can be improved to:
public void CreateBaseObjectInstance<TBase, TSettings>(TSettings baseObjectSettings)
where TBase : BaseObject
where TSettings : BaseObjectSettings, new()
{
if ( baseObjectSettings == null ) baseObjectSettings = new TSettings();
...
}
CreateBaseObjectInstance<Banana, AppleSettings>();
But if there is a strong coupling between entity and settings, you should redesign to define dependency with an association using a thing that can also be similar to #Sweeper's and #Moho's answers:
Association, Composition and Aggregation in C#
Understanding the Aggregation, Association, Composition
Generics in .NET
Generic classes and methods

Class with Iiherited Generic Type convert to class with base Generic Type

I have a class with a generic Type:
class SomeClass<T>
{
}
And two classes -> one inherits fromt the other:
class Animal
{
}
class Lion : Animal
{
}
Now I want to create a variable of "SomeClass" with the base generic and give it an object witht the inherited type as generic type, like this:
void Test()
{
SomeClass<Lion> someLion = new SomeClass<Lion>();
SomeClass<Animal> someAnimal = someLion; //Error line
}
The error is:
Cannot implicitly convert type 'VitrasUI.SomeClass' to 'VitrasUI.SomeClass'
From my understanding I should be able to convert it this way, as it works with IEnumerables, too:
void Test1()
{
IEnumerable<Lion> listLion = new List<Lion>();
IEnumerable<Animal> listAnimal = listLion;
}
With the help of some comments I managed to figure it our quite fast.
What I did:
1) Understand Covariance and Contraviance
(this blog post was pretty easy to understand: http://tomasp.net/blog/variance-explained.aspx/)
2) I added an interface, of which the the abstract class (represented by SomeClass) inherits
3) I used the out keyword to mark the usage of the generic as covariant in the interface

C# Generic Class<T>

How can I add a generic Type to my list?
I tried to create an object of T but this doesn't work neither.
class Bar<T> where T : IDrink
{
List<T> storage = new List<T>();
public void CreateDrink()
{
storage.Add(T); //<- This doesn't work
}
}
T is a type not an instance of that type. So you need a parameter in CreateDrink or use a factory method that returns a new instance of T.
If you want to create an instance the generic constraint must include new()
class Bar<T> where T : IDrink, new()
{
List<T> storage = new List<T>();
public void CreateDrink()
{
storage.Add(new T());
}
}
The new constraint specifies that any type argument in a generic class
declaration must have a public parameterless constructor. To use the
new constraint, the type cannot be abstract.
You can do it like:
storage.Add(Activator.CreateInstance<T>());

What does new() mean?

There is an AuthenticationBase class in WCF RIA Services. The class definition is as follows:
// assume using System.ServiceModel.DomainServices.Server.ApplicationServices
public abstract class AuthenticationBase<T>
: DomainService, IAuthentication<T>
where T : IUser, new()
What does new() mean in this code?
It's the new constraint.
It specifies that T must not be abstract and must expose a public parameterless constructor in order to be used as a generic type argument for the AuthenticationBase<T> class.
Using the new() keyword requires a default constructor to be defined for said class. Without the keyword, trying to class new() will not compile.
For instance, the following snippet will not compile. The function will try to return a new instance of the parameter.
public T Foo <T> ()
// Compile error without the next line
// where T: new()
{
T newInstance = new T();
return newInstance;
}
This is a generic type constraint. See this MSDN article.
It means that a type used to fill the generic parameter T must have a public and parameterless constructor. If the type does not implement such a constructor, this will result in a compile-time error.
If the new() generic constraint is applied, as in this example, that allows the class or method (the AuthenticationBase<T> class in this case) to call new T(); to construct a new instance of the specified type. There is no other way, short of reflection (this includes using System.Activator, to construct a new object of a generic type.

C# Generics - Constraints on type parameters

I'm trying to build a factory method that uses the generics feature of C#.
In this factory method I would like to constraint it to some specific classes, all of which do not have a default constructor.
Here is my example. Can someone tell me if it's possible to run it?
public class AbstractClass {
//this abstract class does not have a default constructor, nor its subclasses
public AbstractClass(SomeClassName obj) {
//use obj for initialization
}
}
//this factory class should create objects of type T that inherit
//from AbstractClass and invoke the non-default constructor
public class FactoryClass {
public static T BuildObject<T> (SomeClassName obj) where T: AbstractClass {
return new T(obj); //does not work?!?!?!
}
}
//Edit: ANSWER!!!
public static T BuildObject<T>(SomeClassUsedForTheConstructor item) where T : SomeAbstractClass {
return (T) Activator.CreateInstance(typeof (T), item);
}
I like to use Activator.CreateInstance(typeof(T)) in my generics that need to create new objects of type T. It works really well.
Look at the Type class and GetConstructor. Once you get the ConstructorInfo object, use the Invoke Method.
var x = typeof(T);
var t = x.GetConstructor(new[] {obj.GetType()});
object u = t.Invoke(<inputs>);
I don't think you can instantiate generic types without a default constructor on the constraint type.
Consider instead specifying an interface IAbstractClass, such that your factory class can set the SomeClassName parameter as a property of IAbstractClass.
Additionally, if a SomeClassName instance is required for initializing AbstractClass, consider also having an empty default constructor, but a rich initializer method defined in IAbstractClass. For example:
public interface IAbstractClass { void Initialize(SomeClassName obj); }
That way, your static BuildObject method instead does:
public static T BuildObject<T>(SomeClassName obj) where T: AbstractClass
{
T newObject = new T();
IAbstractClass ac = newObject as IAbstractClass;
ac.Initialize(obj);
}
No, what you are trying to do is not possible using the built-in generic constraints alone. The new keyword only allows you to constrain the generic type to having a default constructor.

Categories