I have troubles with finding the correct words for this question, so I will try to show you with some code what my problem is.
I have a parent class, which looks like this:
public class ParentClass {
public Guid ParentId { get; }
public int ParentProperty { get; set; }
public List<ParentClass> ParentList { get; set; }
public ParentClass() {
this.ParentId = Guid.NewGuid();
}
}
It is rather simple: It got an ID, a few properties and a List containing elements of itself.
Now I am creating a child class, which looks like this:
public class ChildClass : ParentClass {
public string ChildProperty { get; set; }
public ChildClass() : base() {
this.ParentList = new List<ChildClass>();
}
}
This one got one extra property and a constructor, which contains the problem. I can't initiate a List into the declaration of the List.
I can't just do the declaration of the list in the child class, as I need it in the parent class when I am using it.
What is the best way to solve this problem?
You should use an interface that point both classes (ParentClass as well as ChildClass).
A generic type having a certain type-parameter is a "new" type: So List<ChildClass> and List<ParentClass> are different types.
I think the easiest way to achieve what you want is to initiate the list with its base type : List<ParentClass>
public class ChildClass : ParentClass
{
public string ChildProperty { get; set; }
public ChildClass() : base() {
this.ParentList = new List<ParentClass>();
}
public void AddSomething()
{
// this is ok :
this.ParentList.Add(new ChildClass());
}
}
This could work only if the type List<T> were covariant in T, also known as "out T". However, it is not, and cannot be.
The type List<> allows Add, Insert and others, and so it is not semantically covariant.
In C# (as of currently), class types cannot be made covariant. That is not supported. Only interface and delegate types can be made covariant (or contravariant) in their generic parameters.
The closest we get is IReadOnlyList<out T> which is covariant, so:
IReadOnlyList<ParentClass> parentList = new List<ChildClass>();
is allowed. However, it is not helpful in your case.
public class ParentClass<TChild> where TChild : class
{
public List<TChild> ParentList { get; set; }
public Guid ParentId { get; set; }
public int ParentProperty { get; set; }
public ParentClass()
{
ParentId = Guid.NewGuid();
ParentList = new List<TChild>();
}
}
public class ChildClass : ParentClass<ChildClass>
{
public string ChildProperty { get; set; }
}
Related
I dont think what I am trying to do is possible; is there a way to actually make this work?
There is a Base class from which a variety of different classes are derived. Derived classes can be generic or not; instances of the derived classes are added to a collection of type Base in WindowViewModel. The Base class has a collection of Options that are accessed by the WindowViewModel.
The issue is: the IOption interface declares a return type of Func<object, bool> MyFunc but the return type of MyFunc needs to be Func<T, bool> for the generic class method RunIt() and for the assignment in MyClass to work. I could make the IOption generic, but then the Base class would need to be generic, and then the WindowViewModel.ViewModels would also need to be redefined somehow. I dont want to make the Base generic as introducing generics there just makes everything else a real mess.
Question: is there a different way to declare MyFunc in IOption without using generics to allow assignment of Func<T,bool> in MyClass ?
public interface IOption
{
public string Description {get; set;}
public Expression<Func<object,bool>> MyFunc { get; set; }
}
public class Option : IOption
{
public string Description {get; set;}
public Expression<Func<object,bool>> MyFunc { get; set; }
}
public abstract class Base
{
public abstract ObservableCollection<Option> Options { get; set; }
public abstract Option SelectedOption { get; set; }
public abstract void RunIt();
}
public class Generic<T> : Base
{
private DBContext _context;
public override ObservableCollection<Option> Options { get; set; }
public override Option SelectedOption { get; set; }
public Generic()
: base()
{
Options = new ObservableCollection<Option>();
}
public override void RunIt()
{
var result = _context.Set<T>().Where(SelectedOption?.MyFunc);
// process result
}
}
public class MyClass : Generic<MyType>
{
public MyClass
: base()
{
Func<MyType,bool> expression = t => t.MyDescription = "Hello World";
Options.Add(new Option("Hi", expression)); // fail to compile type mismatch
SelectedOption = Options.First();
}
}
public class Special : Base
{
// do something else
}
public class WindowViewModel
{
public WindowViewModel ()
{
MyViewModels = new ObservableCollection<Base>();
MyViewModels.Add(new Special());
MyViewModels.Add(new MyClass());
}
public ObservableCollection<Base> MyViewModels {get; set;}
public Base SelectedViewModel { get; set; }
public void DoRunIt()
{
SelectedViewModel.RunIt();
}
}
one of the things I did try that compiles but throws runtime exception when used, is
Func<MyType,bool> expression = t => t.MyDescription = "Hello World";
MyFunc = t => expression((MyType)t);
There is a way to do this. It uses the ability for all delegates (Func<MyType, bool> is a delegate) to be cast to Delegate.
You'd change IOption and Option to this:
public interface IOption
{
public string Description { get; set; }
Func<T, bool> GetMyFunc<T>();
}
public class Option : IOption
{
string description;
private Delegate expression;
public Option(string description, Delegate expression)
{
this.description = description;
this.expression = expression;
}
public string Description { get; set; }
public Func<T, bool> GetMyFunc<T>() => (Func<T, bool>)this.expression;
}
Then MyClass works as expected (except for the other syntax error in your code).
You then just need to change RunIt on Generic<T> to this:
public override void RunIt()
{
var result = _context.Set<T>().Where(SelectedOption?.GetMyFunc<T>());
// process result
}
Question: is there a different way to declare MyFunc in IOption without using generics to allow assignment of Func<T,bool> in MyClass ?
No, I don't believe that is possible. You can have generic methods in a non generic type, though.
However, there is an option that might work for you.
You state
I dont want to make the Base generic as introducing generics there just makes everything else a real mess.
How about having both?
public abstract class Base<T>
{
public abstract ObservableCollection<Option<T>> Options { get; set; }
public abstract Option<T> SelectedOption { get; set; }
public abstract void RunIt();
}
public abstract class Base : Base<object> { }
Preface: I am aware that there are a lot of questions and answers about covariance and contravariance but I'm still feeling muddled and not sure what solution to implement.
I have two interfaces whose implementations are intended to be used together in pairs. One provides the information about a sales item and one provides language dependent information for a sales item.
I do not have control over these interfaces:
public interface IItem
{
decimal Price { get; set; }
}
public interface IItemTranslation
{
string DisplayName { get; set; }
}
I also have two implementations of both these interfaces for a tangible GoodsItem as well as an intangible ServiceItem. Again, I do not have control over these interfaces:
public class GoodsItem : IItem
{
public decimal Price { get; set; } //implementation
public float ShippingWeightKilograms { get; set; } //Property specific to a GoodsItem
}
public class GoodsTranslation : IItemTranslation
{
public string DisplayName { get; set; } //implementation
public Uri ImageUri { get; set; } //Property specific to a GoodsTranslation
}
public class ServiceItem : IItem
{
public decimal Price { get; set; } //implementation
public int ServiceProviderId { get; set; } // Property specific to a ServiceItem
}
public class ServiceTranslation : IItemTranslation
{
public string DisplayName { get; set; } //implementation
public string ProviderDescription { get; set; } // Property specific to a ServiceTranslation
}
As I said, these are classes that I do not have control over. I want to create a generic list of these pairings (List<Tuple<IItem, IItemTranslation>>) but I cannot:
public class StockDisplayList
{
public List<Tuple<IItem, IItemTranslation>> Items { get; set; }
public void AddSomeStockItems()
{
Items = new List<Tuple<IItem, IItemTranslation>>();
var canOfBeans = new Tuple<GoodsItem, GoodsTranslation>(new GoodsItem(), new GoodsTranslation());
var massage = new Tuple<ServiceItem, ServiceTranslation>(new ServiceItem(), new ServiceTranslation());
Items.Add(canOfBeans); //illegal: cannot convert from 'Tuple<GoodsItem, GoodsTranslation>' to 'Tuple<IItem, IItemTranslation>'
Items.Add(massage); //illegal: cannot convert from 'Tuple<ServiceItem, ServiceTranslation>' to 'Tuple<IItem, IItemTranslation>' }
}
Question: Without changing my IItem and ITranslation classes or their derived types, what's the cleanest way to be able to pass around a generic list of these pairings without casting them back and forth between the interface and their type?
Caveat. I was trying to simplify the question but I'm not actually using Tuples. In reality I'm using a class like this:
public class ItemAndTranslationPair<TItem, TItemTranslation> where TItem : class, IItem where TItemTranslation : class, IItemTranslation
{
TItem Item;
TTranslation Translation;
}
and my services are returning strongly typed lists, like List<ItemAndTranslationPair<GoodsItem, GoodsTranslation>> and therefore when I add items to the 'generic' list it looks like:
var differentBrandsOfBeans = SomeService.GetCansOfBeans();
//above variable is of type IEnumerable<ItemAndTranslationPair<GoodsItem, GoodsTranslation>>
var items = new List<ItemAndTranslationPair<IItem, IItemTranslation>>();
items.AddRange(differentBrandsOfBeans);
You need to create the tuples using the interface types:
var canOfBeans = new Tuple<IItem, IItemTranslation>(new GoodsItem(), new GoodsTranslation());
var massage = new Tuple<IItem, IItemTranslation>(new ServiceItem(), new ServiceTranslation());
As you're storing them in a list of Tuple<IItem, IItemTranslation> this should be a problem for you.
Use the out modifier on the type parameter of the generic type to obtain covariance in that parameter.
In the current version of C#, this is not supported for class types, only interface types (and delegate types), so you would need to write an interface (notice use of out):
public interface IReadableItemAndTranslationPair<out TItem, out TItemTranslation>
where TItem : class, IItem
where TItemTranslation : class, IItemTranslation
{
TItem Item { get; }
TItemTranslation Translation { get; }
}
Note that the properties cannot have a set accessor since that would be incompatible with the covariance.
With this type, you can have:
var differentBrandsOfBeans = SomeService.GetCansOfBeans();
//above variable is of type
//IEnumerable<IReadableItemAndTranslationPair<GoodsItem, GoodsTranslation>>
var items = new List<IReadableItemAndTranslationPair<IItem, IItemTranslation>>();
items.AddRange(differentBrandsOfBeans);
It works because IEnumerable<out T> is covariant, and your type IReadableItemAndTranslationPair<out TItem, out TItemTranslation> is covariant in both TItem and TItemTranslation.
I have the following classes:
public class ParentClass
{
public string Name {get;set;}
public int Age {get;set;}
}
public class SubClass : ParentClass
{
public int Id {get;set;}
}
and I have the following method:
public void InsertSubClass(ParentClass parentClass)
{
SubClass subClass = new SubClass();
subClass.Id = 1;
subClass.Age = parentClass.Age;
subClass.Name= parentClass.Name;
}
How can I refactor this method in such a way that I dont need to assign the properties of the parameter ParentClass into properties of SubClass one by one?
Are there any alternative which is more efficient? or this is really how to do it? Im just thinking that if the properties are many, this could be tedious..
Thanks in advance. :)
you can achieve this thing by creating copy constructor. Anyways you would have to assign parent class properties somewhere as casting wont work in this case.
Here is copy constructor way that assigns parent property in parent constructor.
public class ParentClass
{
public string Name { get; set; }
public int Age { get; set; }
public ParentClass()
{
}
//Copy constructor
public ParentClass(ParentClass parentClass)
{
this.Name = parentClass.Name;
this.Age = parentClass.Age;
}
}
public class SubClass : ParentClass
{
public int Id { get; set; }
public SubClass(ParentClass parentClass, int id) : base(parentClass)
{
this.Id = id;
}
}
And now method looks like this.
public static void InsertSubClass(ParentClass parentClass)
{
SubClass subClass = new SubClass(parentClass, 1);
}
Update
If you can not make changes to your parent and child class then how about creating an extension method for the parent class in static class like below.
public static void ShallowConvert<T, U>(this T parent, U child)
{
foreach (PropertyInfo property in parent.GetType().GetProperties())
{
if (property.CanWrite)
{
property.SetValue(child, property.GetValue(parent, null), null);
}
}
}
Note: This might not work with private properties and fields.
You can not assign parent class to child's base object like this child.base = parent. Also you can not cast paret class to child like var o = (child)parent; o.id=1; All you can do is add constructor to child class that receives parent class and do work in that constructor.
Create method with access to similar named field, but different types of object without inheritance in c#.
For ex. I have 2 Classes:
public class MyClass1
{
public int Id { get; set; }
}
public class MyClass2
{
public int Id { get; set; }
}
And I need to create method and pass to it instance of each Classes:
DoStuff(new MyClass1());
DoStuff(new MyClass2());
And Method will be like this:
private void DoStuff<T>(T obj)
{
int i = obj.Id(); // here is the problem
}
Yes, problem is, that obj can't resolve Id. Solution simple - create RootClass with Id property, and use public class MyClass1: RootClass... , but I can't do this.
Question is: Can I use line like int i = obj.Id(); without creating RootClass?
Edit: I need answers like: No, because.... or Yes, do this.....
This is typically why interfaces exist: describe a common contract, but with no relationship between the classes.
I would suggest you to create this interface
public interface IHasId {
int Id { get; set; }
}
public class MyClass1 : IHasId
{
public int Id { get; set; }
}
public class MyClass2 : IhasId
{
public int Id { get; set; }
}
...
private void DoStuff<T>(T obj)
where T : IHasId // constraint my be moved to the class declaration
{
int i = obj.Id();
}
Creating this interface will let you avoid introducing a root class, which may be not desired if the classes has no root behavior.
Given the following source types:
public class BaseViewModel
{
public string Prop1 { get; set; }
}
public class FirstViewModelImpl : BaseViewModel
{
public string Prop2 { get; set; }
}
public class SecondViewModelImpl : BaseViewModel
{
public string AnotherProp { get; set; }
}
And the following destination types
public class BaseDto
{
public string Prop1 { get; set; }
}
public class FirstDtoImpl : BaseDto
{
public string Prop2 { get; set; }
}
public class SecondDtoImpl : BaseViewModel
{
public string AnotherProp { get; set; }
}
With the following mappings:
Mapper.CreateMap<FirstViewModelImpl,FirstDtoImpl>();
Mapper.CreateMap<SecondViewModelImpl,SecondDtoImpl>();
Can I do the following (trivial example) - given that I don't actually know the type of viewmodel until runtime?
BaseViewModel myViewModel = GetAViewModelFromSomewhere();
FirstDtoImpl dto = (FirstDtoImpl)Mapper.Map<BaseViewModel,BaseDto>(myViewModel);
I am trying this out now anyway!
I have found that if I change the mappings to
Mapper.CreateMap<BaseViewModel,BaseDto>()
.Include<FirstViewModelImpl,FirstDtoImpl>()
.Include<SecondViewModelImpl,SecondDtoImpl>();
Mapper.CreateMap<FirstViewModelImpl,FirstDtoImpl>();
Mapper.CreateMap<SecondViewModelImpl,SecondDtoImpl>();
Then it works as expected without using the type converter.
You can't do that directly, however you can work around it with a TypeConverter.
In the Mappings you will add:
Mapper.CreateMap<BaseViewModel, BaseDto>()
.ConvertUsing<MyTypeConverter>();
Then you can create the converter like so:
public class MyTypeConverter : TypeConverter<BaseViewModel, BaseDto>
{
protected override BaseDto ConvertCore(BaseViewModel tViewModel)
{
BaseDto vResult = null;
if(tViewModel is FirstViewModelImpl)
{
var vSource = tViewModel as FirstViewModelImpl;
vResult = Mapper.Map<FirstViewModelImpl,FirstDtoImpl>(vSource);
}
else if(tViewModel is SecondViewModelImpl )
{
var vSource = tViewModel as SecondViewModelImpl ;
vResult = Mapper.Map<SecondViewModelImpl ,SecondDtoImpl>(vSource);
}
return vResult;
}
}
Then you can use it like:
BaseDto dto= Mapper.Map<BaseViewModel,BaseDto>(myViewModel);
and have dto actually be of the type you wanted.
It won't map the Base types to each other though. If that matters I can twist it a bit more.
Can you use an interface instead? Also, there is a non-generic Mapper.Map implimentation that might work better in this case. If you have the mapping set up, you can just pass in the type.
No this is not correct if you create Mapping for derived types you should when
map objects specify the derived class also