I've this class structure:
namespace ClassLibrary1
{
public interface IComponentGuid { }
class ComponentGuid : IComponentGuid{}
internal interface IComponent<T> where T : IComponentGuid {
List<T> List();
}
class SpecificComponent : IComponent<ComponentGuid> {
public List<ComponentGuid> List()
{
throw new System.NotImplementedException();
}
}
class P
{
public P(IComponent<IComponentGuid> pComponent) { }
}
class Caller
{
public Caller()
{
var specific = new SpecificComponent();
var p = new P(specific);
}
}
}
The problem arise instantiating P: var p = new P(specific);
I get a
cannot convert from 'ClassLibrary1.SpecificComponent' to 'ClassLibrary1.IComponent<ClassLibrary1.IComponentGuid>'
What am I doing wrong?
Thank you.
You can make it work if you forgo List<T> on your interface and replace it with a co-variant interface of IEnumerable<T> and then make your type parameter co-variant as well:
namespace ClassLibrary1
{
public interface IComponentGuid { }
class ComponentGuid : IComponentGuid{}
internal interface IComponent<out T> where T : IComponentGuid {
IEnumerable<T> List();
}
class SpecificComponent : IComponent<ComponentGuid> {
public IEnumerable<ComponentGuid> List()
{
throw new System.NotImplementedException();
}
}
class P
{
public P(IComponent<IComponentGuid> pComponent) { }
}
class Caller
{
public Caller()
{
var specific = new SpecificComponent();
var p = new P(specific);
}
}
}
I'm trying this solution splitting the IComponent interface into two, one covariant and one invariant.
namespace ClassLibrary1
{
public interface IComponentGuid { }
public class ComponentGuid : IComponentGuid { }
public interface IComponentBase<out T> where T : IComponentGuid
{
IEnumerable<T> List();
}
interface IComponent<T>
{
void AddToList(T item );
}
public class SpecificComponent : IComponentBase<ComponentGuid>, IComponent<ComponentGuid>
{
public IEnumerable<ComponentGuid> List()
{
throw new System.NotImplementedException();
}
public void AddToList(ComponentGuid item)
{
throw new System.NotImplementedException();
}
}
public class P
{
public P(IComponentBase<IComponentGuid> pComponentBase) { }
}
class Caller
{
public Caller()
{
var specific = new SpecificComponent();
var p = new P(specific);
}
}
}
Related
I have following code that does not compile
using System.Collections.Generic;
public interface IElement
{
}
public class AElement : IElement
{
public void DoSomethingSpecial()
{ }
}
public class Container<TElement>
{
public List<TElement> Elements { get; } = new();
}
public class Program
{
public static Container<IElement> GetContainer()
{
var concreteContainer = new Container<AElement>();
concreteContainer.Elements.ForEach(e => e.DoSomethingSpecial());
return concreteContainer; // Cannot implicitly convert type 'Container<AElement>' to 'Container<IElement>'
}
public static void Main()
{
var myContainer = GetContainer();
}
}
I read documentation about Covariance, Invariance, Contravariance and out Types.
And I am more confused than at the beginning.
Whats the way to fix this?
Code online: https://dotnetfiddle.net/85AgfT
You need to generate implicit conversion operator:
public class Container<IElement>
{
public List<IElement> Elements { get; } = new List<IElement>();
public static implicit operator Container<IElement>(Container<AElement> v)
{
//here you need to create Container<IElement> with your Container<AElement> 'v' values
return new Container<IElement>();
}
}
I finally got it working
using System.Collections.Generic;
public interface IContainer<out TElement>
{
}
public interface IElement
{
}
public class AElement : IElement
{
public void DoSomethingSpecial()
{ }
}
public class Container<TElement> : IContainer<TElement>
{
public List<TElement> Elements { get; } = new();
}
public class Program
{
public static IContainer<IElement> GetContainer()
{
var concreteContainer = new Container<AElement>();
concreteContainer.Elements.ForEach(e => e.DoSomethingSpecial());
return concreteContainer;
}
public static void Main()
{
var myContainer = GetContainer();
}
}
Make Container also an Interface and use an out Type parameter
IQueryable extension method is fine in normal method, but not within a generic method.
Compile error: IQueryable does not contain a definition for MyExtension and the best extension method overload DataExtensions.MyExtension(IQueryable Fred) requires a receiver of type IQueryable Fred
Goal is turn the // do something interesting below into a generic method that would work acrross all FlintstoneObject types.
public static class RepetitiveCodeBelow
{
public static int RepetitiveCode()
{
var count = 0;
using (var context = new DataContext())
{
foreach (var data in context.Freds.AsNoTracking().Where(item => item.PrimaryKey > 0).MyExtension())
{
// do something interesting
}
foreach (var data in context.Barnies.AsNoTracking().Where(item => item.PrimaryKey > 0).MyExtension())
{
// do something interesting
}
// more types omitted
}
return count;
}
}
Working version:
public List<Fred> GetFredList()
{
using (var context = new DataContext())
{
return context.Freds.AsNoTracking().MyExtension().ToList();
}
}
Wont compile:
public List<T> GetList<T>() where T : FlintstoneObject<T>, new()
{
using (var context = new DataContext())
{
return context.Set<T>().AsNoTracking().MyExtension().ToList();
}
}
Full sample
public abstract class FlintstoneObject<T> where T : class
{
public abstract int PrimaryKey { get; set; }
}
public class Fred : FlintstoneObject<Fred>
{
public override int PrimaryKey { get; set; }
}
public class Barny : FlintstoneObject<Barny>
{
public override int PrimaryKey { get; set; }
}
public static class DataExtensions
{
public static IQueryable<Fred> MyExtension(this IQueryable<Fred> queryable)
{
return queryable;
}
}
public class DataContext : DbContext
{
public DbSet<Fred> Freds { get; set; }
public DbSet<Barny> Barnies { get; set; }
public List<Fred> GetFredList()
{
using (var context = new DataContext())
{
return context.Freds.AsNoTracking().MyExtension().ToList();
}
}
public List<T> GetList<T>() where T : FlintstoneObject<T>, new()
{
using (var context = new DataContext())
{
return context.Set<T>().AsNoTracking().MyExtension().ToList();
}
}
}
Your code is probably lacking of this extension:
public static IQueryable<T> MyExtension<T>(this IQueryable<T> queryable)
{
return queryable;
}
to make it compilable.
Anyway, what you intended to do with your generic stuff looks strange:
public class Fred : FlintstoneObject<Fred>
I thought T should be some class other than Fred which is inheriting the generic type FlintstoneObject<T>.
The problem I'm actually working on is related to mappers in ASP.NET MVC but that's way too complex to post on SO, so I've simplified the issue I'm having below. I'll post my code first as it's easier to explain what I'm trying to achieve after the code.
Supporting Code
public abstract class BaseFoo
{
public int CommonProperty { get; set; }
}
public class Foo1 : BaseFoo
{
public int SomeProperty { get; set; }
}
public class Foo2 : BaseFoo
{
public int AnotherProperty { get; set; }
}
public interface IMyInterface<T>
{
void SomeMethod(T t);
}
public abstract class BaseClass<T> : IMyInterface<T>
where T : BaseFoo
{
public virtual void SomeMethod(T t)
{
t.CommonProperty = 1;
}
}
public class ConcreteClass1 : BaseClass<Foo1>
{
public override void SomeMethod(Foo1 t)
{
t.SomeProperty = 57;
base.SomeMethod(t);
}
}
public class ConcreteClass2 : BaseClass<Foo2>
{
public override void SomeMethod(Foo2 t)
{
t.AnotherProperty = 123;
base.SomeMethod(t);
}
}
public static class ConcreteClassFactory
{
public enum ConcreteClassType
{
ConcreteClass1,
ConcreteClass2
}
public static dynamic CreateClass(ConcreteClassType type)
{
dynamic toReturn = null;
switch (type)
{
case ConcreteClassType.ConcreteClass1:
toReturn = new ConcreteClass1();
break;
case ConcreteClassType.ConcreteClass2:
toReturn = new ConcreteClass2();
break;
default:
break;
}
return toReturn;
}
}
What I want to do is dynamically create different ConcreteClasss and call SomeMethod on that created object, basically I want to pass around my ConcreteClasss as BaseClass, much like you can pass around Foos as BaseFoo. I've gotten it to work with the following code:
class Program
{
static void Main(string[] args)
{
BaseFoo foo = new Foo1();
dynamic bar = ConcreteClassFactory.CreateClass(ConcreteClassFactory.ConcreteClassType.ConcreteClass1);
bar.SomeMethod(foo as dynamic);
}
}
However, this seems very kludgy to cast to a dynamic (also I don't fully understand why removing as dynamic throws a RuntimeBinderException, if someone can explain what's going on that would be appreciated). Is there a better way to achieve what I'm trying to do here?
With the constraints you have what I would do would be to cast and throw errors from inside the overridden SomeMethod.
public abstract class BaseClass : IMyInterface<BaseFoo>
{
public virtual void SomeMethod(BaseFoo t)
{
t.CommonProperty = 1;
}
}
public class ConcreteClass1 : BaseClass
{
public override void SomeMethod(BaseFoo t)
{
if(t == null)
throw new ArgumentNullException(nameof(t));
var foo1 = t as Foo1;
if(foo1 == null)
throw new NotSupportedException($"{nameof(ConcreteClass1)} does not support types other than {nameof(Foo1)}");
foo1.SomeProperty = 57;
base.SomeMethod(foo1);
}
}
public class ConcreteClass2 : BaseClass
{
public override void SomeMethod(BaseFoo t)
{
if (t == null)
throw new ArgumentNullException(nameof(t));
var foo2 = t as Foo2;
if (foo2 == null)
throw new NotSupportedException($"{nameof(ConcreteClass2)} does not support types other than {nameof(Foo2)}");
foo2.AnotherProperty = 123;
base.SomeMethod(foo2);
}
}
public static class ConcreteClassFactory
{
public enum ConcreteClassType
{
ConcreteClass1,
ConcreteClass2
}
public static BaseClass CreateClass(ConcreteClassType type)
{
BaseClass toReturn = null;
switch (type)
{
case ConcreteClassType.ConcreteClass1:
toReturn = new ConcreteClass1();
break;
case ConcreteClassType.ConcreteClass2:
toReturn = new ConcreteClass2();
break;
default:
break;
}
return toReturn;
}
}
Used like
class Program
{
static void Main(string[] args)
{
BaseFoo foo = new Foo1();
var bar = ConcreteClassFactory.CreateClass(ConcreteClassFactory.ConcreteClassType.ConcreteClass1);
bar.SomeMethod(foo);
}
}
Is it possible to cast T in the line new ConsumeA1<T> (); to A1?
With the current code I'm getting the error:
Type 'T' cannot be used as parameter 'T' in the generic type or method 'ConsumeA1'. There is no boxing conversion or type parameter conversion from 'T' to 'A1'
class Consume<T> where T : A
{
public Consume()
{
if (typeof(T) == typeof(A1))
{
new ConsumeA1<T> ();
}
if (typeof(T) == typeof(A))
{
new ConsumeA<T>();
}
}
}
class ConsumeA<T> where T : A
{
}
class ConsumeA1<T> where T : A1
{
}
interface A
{
}
interface A1 : A
{
}
class Imp1 : A
{
}
class Imp2 : A1
{
}
// ....
class ImpN : A
{
}
There is not point in using generics if you want to cast to a concrete type...
From what I understand all your concrete types contain a Consume() functionality
You can move this method to be part of your A interface.
Then,you don't need a Consume< T> class.
You can just use a method that accepts a A as a parameter and calls Consume()
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
A obj = new Imp1();
Consume(obj);
obj = new Imp2();
Consume(obj);
obj = new ImpN();
Consume(obj);
Console.ReadLine();
}
static void Consume(A a) { a.Consume(); }
}
interface A { void Consume(); }
interface A1 : A { }
class Imp1 : A { public void Consume(){ Console.WriteLine("Imp1"); } }
class Imp2 : A1 { public void Consume(){ Console.WriteLine("Imp2"); } }
class ImpN : A { public void Consume(){ Console.WriteLine("ImpN"); } }
}
Now if you need something similar to your sample code you can use generic constraints
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Consume<Imp1>.ConsumeObject();
Consume<Imp2>.ConsumeObject();
Consume<ImpN>.ConsumeObject();
Console.ReadLine();
}
}
class Consume<T> where T : A, new()
{
static public void ConsumeObject()
{
new T();
}
}
class ConsumeA<T> where T : A { }
class ConsumeA1<T> where T : A1 { }
interface A { }
interface A1 : A { }
class Imp1 : A { public Imp1() { Console.WriteLine("Imp1"); } }
class Imp2 : A1 { public Imp2() { Console.WriteLine("Imp2"); } }
class ImpN : A { public ImpN() { Console.WriteLine("ImpN"); } }
}
or with fewer lines of code
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
new Consume<Imp1>();
new Consume<Imp2>();
new Consume<ImpN>();
}
}
class Consume<T> where T : A, new() { public Consume() { new T(); } }
interface A { }
interface A1 : A { }
class Imp1 : A { public Imp1() { System.Console.WriteLine("Imp1"); } }
class Imp2 : A1 { public Imp2() { System.Console.WriteLine("Imp2"); } }
class ImpN : A { public ImpN() { System.Console.WriteLine("ImpN"); } }
}
or without the Consume< T >class
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Consume<Imp1>();
Consume<Imp2>();
Consume<ImpN>();
System.Console.ReadLine();
}
private static void Consume<T>() where T : A, new() { new T(); }
}
interface A { }
interface A1 : A { }
class Imp1 : A { public Imp1() { System.Console.WriteLine("Imp1"); } }
class Imp2 : A1 { public Imp2() { System.Console.WriteLine("Imp2"); } }
class ImpN : A { public ImpN() { System.Console.WriteLine("ImpN"); } }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace EfTestFactory
{
public abstract class _Company
{
public abstract List<Personel> GetPersonel();
public abstract List<Prim> GetPrim();
public abstract List<Finans> GetFinans();
}
public abstract class _Radyoloji
{
public abstract List<string> GetRadyoloji();
}
public abstract class _Satis
{
public abstract List<string> GetSatis();
}
public abstract class _Muayene
{
public abstract List<string> GetMuayene();
}
public class Company : _Company
{
public override List<Personel> GetPersonel()
{
throw new NotImplementedException();
}
public override List<Prim> GetPrim()
{
throw new NotImplementedException();
}
public override List<Finans> GetFinans()
{
throw new NotImplementedException();
}
}
public class Radyoloji : _Radyoloji
{
public override List<string> GetRadyoloji()
{
throw new NotImplementedException();
}
}
public class Satis : _Satis
{
public override List<string> GetSatis()
{
throw new NotImplementedException();
}
}
public class Muayene : _Muayene
{
public override List<string> GetMuayene()
{
throw new NotImplementedException();
}
}
public class GenoTipController
{
public object CreateByEnum(DataModelType modeltype)
{
string enumText = modeltype.ToString(); // will return for example "Company"
Type classType = Type.GetType(enumText); // the Type for Company class
object t = Activator.CreateInstance(classType); // create an instance of Company class
return t;
}
}
public class AntsController
{
static Dictionary<DataModelType, Func<object>> s_creators =
new Dictionary<DataModelType, Func<object>>()
{
{ DataModelType.Radyoloji, () => new _Radyoloji() },
{ DataModelType.Company, () => new _Company() },
{ DataModelType.Muayene, () => new _Muayene() },
{ DataModelType.Satis, () => new _Satis() },
};
public object CreateByEnum(DataModelType modeltype)
{
return s_creators[modeltype]();
}
}
public class CompanyView
{
public static List<Personel> GetPersonel()
{
GenoTipController controller = new GenoTipController();
_Company company = controller.CreateByEnum(DataModelType.Company) as _Company;
return company.GetPersonel();
}
}
public enum DataModelType
{
Radyoloji,
Satis,
Muayene,
Company
}
}
if i write above codes i see some error: Cannot create an instance of abstract class or interface 'EfTestFactory_Company'How can i solve it? Look please below pic.
The purpose of the keyword abstract is to make a class not instantiable. If you have declared a class abstract and you want to instantiate it, then you have a design flaw.
You cannot instantiate abstract classes. You could instantiate only concrete classes:
static Dictionary<DataModelType, Func<object>> s_creators =
new Dictionary<DataModelType, Func<object>>()
{
{ DataModelType.Radyoloji, () => new Radyoloji() },
{ DataModelType.Company, () => new Company() },
{ DataModelType.Muayene, () => new Muayene() },
{ DataModelType.Satis, () => new Satis() },
};