I have three base classes:
public class ItemBase
{
public string Name { get; set; }
}
public class ProductBase<T> : ItemBase
where T : ItemBase
{
public List<T> Modifiers { get; set; }
public List<T> GroupModifiers { get; set; }
}
public class ModifierBase<T> : ItemBase
where T : ItemBase
{
public List<T> ChildModifiers { get; set; }
}
And two derived classes:
public class Product : ProductBase<Modifier>
{
public string Some_Product_Specific_Property { get; set; }
}
public class Modifier : ModifierBase<Modifier>
{
public string Some_Modifier_Specific_Property { get; set; }
}
The intent behind all this is to have different sets of derived classes like Product and ProductFromOtherSystem each with it's own specific properties but with the same basic properties.
And now I need to process basic properties of any class derived from ProductBase<T>.
For this purpose I want to use something like this:
public static void DoSomething(ProductBase<ModifierBase<ItemBase>> item)
{
Console.WriteLine(item.Name);
}
The issue here is that I cannot pass parameters to it:
Product product1 = new Product();
DoSomething(product1);
ProductFromOtherSystem product2 = new ProductFromOtherSystem();
DoSomething(product2 as ProductBase<ModifierBase<ItemBase>>);
The error is like
Cannot convert type _ to _ via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion
I've tried to downcast it somehow but have not found any solution. I wonder if it is possible to do it?
Related
I am trying to create a list of a class with the property with T:
class Test<T> where T : IClass, new()
{
public T Actor { get { return new T(); } }
public eEnum { get; set; }
public String Str { get; set; }
}
The above is an example class, how can I create a list of the above class?
I have tried the below with no avail:
List<Test<IClass>> list = new List<IClass>();
Is there a way to achieve creating a list like I am trying to generate?
Since you have added the generic type constraint new(), you must provide a type that has a public parameterless constructor. An interface doesn't have a constructor. Therefore you must indicate a class. E.g.
List<Test<MyActorClass>> list = new List<Test<MyActorClass>>();
Or drop this new() constraint and instead add a generic factory method
class Test
{
public T CreateActor<T>()
where T : IClass, new()
{
return new T();
}
public MyEnum eEnum { get; set; }
public string Str { get; set; }
}
And simply create a list List<Test>.
Or supply a concrete actor type through constructor injection:
class Test
{
public Test(IClass actor)
{
Actor = actor;
}
public IClass Actor { get; }
public MyEnum eEnum { get; set; }
public string Str { get; set; }
}
An even more advanced construction is to use a non-generic abstract base class and to derive a generic one from it
abstract class Test
{
private IClass _actor;
public IClass Actor
{
get {
if (_actor == null) {
_actor = CreateActor();
}
return _actor;
}
}
public MyEnum eEnum { get; set; }
public string Str { get; set; }
protected abstract IClass CreateActor(); // We implement it in the generic class.
}
class Test<T> : Test
where T : IClass, new()
{
public new T Actor // Hides inherited member.
{
get { return (T)base.Actor; }
}
protected override IClass CreateActor()
{
return new T();
}
}
The list would again be of type List<Test>. This has the advantage that you can add different types of classes deriving from Test to the list and at the same time you have a strongly typed actor when accessing it through a concrete Test<T>.
I have an interface
public interface IIdentity<T>
{
T GetUser();
}
I have a base class that implements the Interface as an abstract method
public abstract class BaseUser<T> : IIdentity<T>
{
public string UserId { get; set; }
public string AuthType { get; set; }
public List<Claim> Claims { get; set; }
public abstract T GetUser();
}
In the class that inherits the base class
public class JwtUser : BaseUser
{
public string Sub { get; set; }
}
I get an error using the generic type BaseUser requires 1 argument, what do i do here, basically I'd like my user to inherit shared properties from the base class which it does (i think) and to implement the generic method from the base class as I'm going to have different types of users (JWT/Windows etc) I need to abstract away the getUsers method, hope that makes sense ?
You have to ways to implement this, both require to set the generic in BaseUser.
You could expose that generic:
public class JwtUser<T> : BaseUser<T>
{
public string Sub { get; set; }
}
Or, just set the generic:
public class JwtUser : BaseUser<JwtUser>
{
public string Sub { get; set; }
}
it should be like , for Ref : Generic Classes (C# Programming Guide)
public class JwtUser<User> : BaseUser<User>
{
public string Sub { get; set; }
}
or
public class JwtUser<T> : BaseUser<T>
{
public string Sub { get; set; }
}
and when create instace
var jwtUser =new JwtUser<User> ();
or
class JwtUser: BaseUser<JwtUser> { }
In all way at the end you must need to specify value for T template as its generic.
For example if you take List<T> for using it you must need to intialize with proper type like if interger list then List<int> intlist = new List<int>();
Let's say I have nested generic data classes similar to the following:
public class BaseRecordList<TRecord, TUserInfo>
where TRecord : BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual IList<TRecord> Records { get; set; }
public virtual int Limit { get; set; }
}
public class BaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
public class BaseUserInfo
{
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
With 2 concrete versions like so:
// Project 1: Requires some extra properties
public class Project1RecordList : BaseRecordList<Project1Record, Project1UserInfo> {}
public class Project1Record : BaseRecord<Project1UserInfo>
{
public Guid Version { get; set; }
}
public class Project1UserInfo : BaseUserInfo
{
public string FavouriteFood { get; set; }
}
and
// Project 2: Some properties need alternate names for JSON serialization
public class Project2RecordList : BaseRecordList<Project2Record, Project2UserInfo>
{
[JsonProperty("allRecords")]
public override IList<Project2Record> Records { get; set; }
}
public class Project2Record : BaseRecord<Project2UserInfo> {}
public class Project2UserInfo : BaseUserInfo
{
[JsonProperty("username")]
public override string Name { get; set; }
}
I'm then happy to have 2 repositories that return Project1RecordList and Project2RecordList respectively, but at some point in my code I find myself needing to be able to handle both of these in one place. I figure that at this point I need to be able to treat both of these types as
BaseRecordList<BaseRecord<BaseUserInfo>, BaseUserInfo>
as this is the minimum required to meet the generic constraints, but trying to cast or use "as" throws up errors about not being able to convert.
Is there any way to do this, or even a more sane way to handle this situation without massive amounts of code duplication? If it makes any difference this is for a web app and there are already a large number of data classes, many of which use these nested generics.
What you are talking about is called covariance and MSDN has a great article on this here: https://msdn.microsoft.com/en-us/library/dd799517(v=vs.110).aspx
First, create a new interface:
interface IBaseRecord<out TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecord inherit from the new interface:
public class BaseRecord<TUserInfo> : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
public virtual DateTime CreationTime { get; set; }
public virtual TUserInfo UserInfo { get; set; }
}
If done right, this should compile:
IBaseRecord<BaseUserInfo> project1 = new Project1Record();
IBaseRecord<BaseUserInfo> project2 = new Project2Record();
To expand this to the BaseRecordList, create IBaseRecordList:
interface IBaseRecordList<out TRecord, out TUserInfo>
where TRecord : IBaseRecord<TUserInfo>
where TUserInfo : BaseUserInfo
{
}
Have BaseRecordList inherit from that:
public class BaseRecordList<TRecord, TUserInfo> : IBaseRecordList<TRecord, TUserInfo>
And then use as such:
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project1 = new Project1RecordList();
IBaseRecordList<IBaseRecord<BaseUserInfo>, BaseUserInfo> project2 = new Project2RecordList();
Once you have that setup, just add whatever properties or functions you need to use generically to the interfaces.
I created the following abstract class:
public abstract class AbstractClass
{
public abstract string Name { get; set; }
public abstract object Value { get; set; }
}
Now I want to derive two classes of the abstract class. I want to use an enum instead of the type object. My derived classes look like this:
First class:
public class InheritanceClass1:AbstractClass
{
public override string Name { get; set; }
public override FirstEnum Value { get; set; }
}
Second class:
public class InheritanceClass2 : AbstractClass
{
public override string Name { get; set; }
public override SecondEnum Value { get; set; }
}
I'm getting an error showed in my code, that the type of the property Value isn't object. I tryed to use the new-keyword instead of override like this:
In my abstract class:
public object Value { get; set; }
In my derived class:
public new FirstEnum Value { get; set; }
But if I create a List<AbstractClass> I have the problem that I can't use it for example for Linq because I would retrieve the "wrong" property. It is just hided, but still there, so I have to override the property.
So how do I have to change my abstract class and my derived classes, that I can use different types in my derived classes?
You can use abstract class like this:
public abstract class AbstractClass<T>
{
public abstract string Name { get; set; }
public abstract T Value { get; set; }
}
And derived class will change like this:
public class InheritanceClass1 : AbstractClass<FirstEnum>
{
public override string Name { get; set; }
public override FirstEnum Value { get; set; }
}
If you know that you will need only enums, you can add struct, IConvertible restriction to T:
public abstract class AbstractClass<T> where T : struct, IConvertible
{
public abstract string Name { get; set; }
public abstract T Value { get; set; }
}
Update based on comment:
Not the cleanest solution if you need List<AbstractClass>, but you can have additional class:
public abstract class AbstractClass
{
public abstract string Name { get; set; }
public abstract int GetValue ();
}
Which will then be inherited by AbstractClass<T>:
public abstract class AbstractClass<T> : AbstractClass where T : struct, IConvertible
{
public abstract T Value { get; set; }
}
And InheritancClass:
public class InheritanceClass1 : AbstractClass<FirstEnum>
{
public override string Name { get; set; }
public override FirstEnum Value { get; set; }
public override int GetValue () => (int)Value;
}
And then you can use it in a list:
var list = new List<AbstractClass> { new InheritanceClass1 (), new InheritanceClass2 () };
In this way you can use List<AbstractClass> with GetValue method. If you are using only enums you can always recast it to enum value. Ofcorse, you would not know exactly which enum it is, but you can add additional field for that.
I'm getting the error "T does not contain a definition for Id" below in the specified line, even though when I debug, I see that "item" does indeed have a property "Id" in its base class.
How do I specify here that I want C# to look in the item's base class for Id (and why doesn't it do this automatically?)?
//public abstract class Items<T> : ItemBase (causes same error)
public abstract class Items<T> where T : ItemBase
{
public List<T> _collection = new List<T>();
public List<T> Collection
{
get
{
return _collection;
}
}
public int GetNextId()
{
int highestId = 0;
foreach (T item in _collection)
{
//ERROR: "T does not contain a definition for Id
if (item.Id > highestId) highestId = item.Id;
}
return highestId;
}
}
Here's how the classes are being defined:
public class SmartForm : Item
{
public string IdCode { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int LabelWidth { get; set; }
public int FormWidth { get; set; }
...
public abstract class Item : ItemBase
{
public int Id { get; set; }
public DateTime WhenCreated { get; set; }
public string ItemOwner { get; set; }
public string PublishStatus { get; set; }
public int CorrectionOfId { get; set; }
...
Your problem is that there is no constraint on T, so, at compile time, all the compiler knows is that T is some sort of object. If you know what type T will always inherit off, you can add a generic constraint to the class definition:
public abstract class Items<T> : ItemBase where T : Item
{
//....
}
When you're debugging, T is instantiated to an Item (or subclass of) that does have an Id property, but the compiler doesn't know that at compile time, hence the error.
You got your generic constraint wrong.
public abstract class Items<T> : ItemBase
should be:
public abstract class Items<T> where T : ItemBase
What happened is that while your class has a collection of items, your ItemBase is not associated with T
Because T could be absolutely anything, and C# is meant to be type-safe. Use a constraint on T to a type which has an id property, and you should be OK. See here for more info.
You have to specify that T is always derived from ItemBase using a where clause.
You accidentally let Items inherit ItemBase, instead of T inheriting ItemBase.
Just change
: ItemBase
to
where T : ItemBase
this might help :-)
foreach (ItemBase item in _collection)
{
if (item.Id > highestId) highestId = (Item)item.Id;
}