How can I restructure my code to get rid of the runtime error happening at the point indicated?
DataSeries<SimpleDataPoint> needs to be able to cast back to IDataSeries<IDataPoint> somehow
I have tried using inheritance of two interfaces, like this:
public class DataSeries<TDataPoint> : IDataSeries<TDataPoint>, IDataSeries<IDataPoint> but received compiler error:
'DataSeries<TDataPoint>' cannot implement both
'IDataSeries<TDataPoint>' and
'IDataSeries<IDataPoint>'
because they may unify for some type parameter substitutions
Using covariance doesn't seem to be an option because I can't make the interfaces covariant or contravariant.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1 {
class Program {
[STAThread]
static void Main(string[] args) {
var source = new object();
// compiles fine, but ...
// runtime error here - cannot cast
var ds = (IDataSeries<IDataPoint>)new DataSeries<SimpleDataPoint>(source);
Console.ReadKey();
}
}
public interface IDataPoint {
int Index { get; set; }
double Value { get; set; }
DateTime TimeStampLocal { get; set; }
IDataPoint Clone();
}
public sealed class SimpleDataPoint : IDataPoint {
public int Index { get; set; }
public double Value { get; set; }
public DateTime TimeStampLocal { get; set; }
public IDataPoint Clone() {
return new SimpleDataPoint {
Index = Index,
Value = Value,
TimeStampLocal = TimeStampLocal,
};
}
}
public interface IDataSeries<TDataPoint> where TDataPoint : class, IDataPoint {
object Source { get; }
int Count { get; }
double GetValue(int index);
DateTime GetTimeStampLocal(int index);
TDataPoint GetDataPoint(int index);
TDataPoint GetLastDataPoint();
void Add(TDataPoint dataPoint);
IDataSeries<TDataPoint> Branch(object source);
}
public class DataSeries<TDataPoint> : IDataSeries<TDataPoint> where TDataPoint : class, IDataPoint {
readonly List<TDataPoint> _data = new List<TDataPoint>();
public object Source {
get;
private set;
}
public DataSeries(object source) {
Source = source;
}
public int Count {
get { return _data.Count; }
}
public TDataPoint GetDataPoint(int index) {
return _data[index];
}
public TDataPoint GetLastDataPoint() {
return _data[_data.Count - 1];
}
public DateTime GetTimeStampLocal(int index) {
return _data[index].TimeStampLocal;
}
public double GetValue(int index) {
return _data[index].Value;
}
public void Add(TDataPoint dataPoint) {
_data.Add(dataPoint);
}
public IDataSeries<TDataPoint> Branch(object source) {
throw new NotImplementedException();
}
}
}
The problem is that new DataSeries<SimpleDataPoint> is not IDataSeries<IDataPoint>, because calling IDataSeries<IDataPoint>.Value = new AnotherDataPoint() and IDataPoint value = IDataSeries<IDataPointBase>.Value can fail. That is, runtime can't guarantee that what you're doing is type-safe, so it throws an exception to tell you that. Runtime can only guarantee one of these operations to be safe only if your interface is marked as covariant or contravariant. It's marked as neither, so it isn't type-safe, so it can't be done.
If you intend to bypass type safety, you can create an unsafe proxy:
public class DataSeries<TDataPoint> : IDataSeries<TDataPoint>
where TDataPoint : class, IDataPoint
{
// ...
public IDataSeries<IDataPoint> GetUnsafeProxy ()
{
return new UnsafeProxy(this);
}
private class UnsafeProxy : IDataSeries<IDataPoint>
{
private readonly DataSeries<TDataPoint> _owner;
public UnsafeProxy (DataSeries<TDataPoint> owner)
{
_owner = owner;
}
public object Source
{
get { return _owner.Source; }
}
public int Count
{
get { return _owner.Count; }
}
public double GetValue (int index)
{
return _owner.GetValue(index);
}
public DateTime GetTimeStampLocal (int index)
{
return _owner.GetTimeStampLocal(index);
}
public IDataPoint GetDataPoint (int index)
{
return _owner.GetDataPoint(index);
}
public IDataPoint GetLastDataPoint ()
{
return _owner.GetLastDataPoint();
}
public void Add (IDataPoint dataPoint)
{
_owner.Add((TDataPoint)dataPoint);
}
public IDataSeries<IDataPoint> Branch (object source)
{
return (IDataSeries<IDataPoint>)_owner.Branch(source);
}
}
You can use this proxy like this:
IDataSeries<IDataPoint> ds = new DataSeries<SimpleDataPoint>(source).GetUnsafeProxy();
Note that the last two methods use type casting, so calling them is not safe, they can throw in case of incompatible types. If want to cast DataSeries not only to base types, but to other types too, you'll have to add even more type casts to the unsafe proxy and lose even more type safety. The choice is yours.
So my question got me thinking about code smells, and things like "What am I really trying to achieve?"
Well, here's what I figured out I want to achieve: I want to convert DataSeries<TDataPoint> to IReadOnlyDataSeries<IDataPoint> only when I am passing it as an input to a class that processes read-only data from a IReadonlyDataSeries<IDataPoint> object.
Here's the important change made:
// here's the covariant, read-only part of the interface declaration
public interface IReadOnlyDataSeries<out TDataPoint> where TDataPoint : class, IDataPoint {
object Source { get; }
int Count { get; }
double GetValue(int index);
DateTime GetTimeStampLocal(int index);
TDataPoint GetDataPoint(int index);
TDataPoint GetLastDataPoint();
}
// add a few bits to the read-write fully-typed interface, breaking covariance,
// but being able to implicitly cast to the covariant readonly version when needed
public interface IDataSeries<TDataPoint> : IReadOnlyDataSeries<TDataPoint> where TDataPoint : class, IDataPoint {
void Add(TDataPoint dataPoint);
IDataSeries<TDataPoint> Branch(object source);
}
Here's the full version of the revised code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1 {
class Program {
[STAThread]
static void Main(string[] args) {
var source = new object();
// implicit conversion works great!!
// therefore I can achieve the goal of passing the fully-typed read-write dataseries
// into objects that just want simple read-only data
var inputSeries = new DataSeries<SimpleDataPoint>(source);
// passing inputSeries into the constructor involves an implicit
// conversion to IReadOnlyDataSeries<IDataPoint>
var processor = new DataProcessor(inputSeries);
Console.ReadKey();
}
public class DataProcessor {
IReadOnlyDataSeries<IDataPoint> InputSeries;
DataSeries<SimpleDataPoint> OutputSeries;
public DataProcessor(IReadOnlyDataSeries<IDataPoint> inputSeries) {
InputSeries = inputSeries;
OutputSeries = new DataSeries<SimpleDataPoint>(this);
}
}
}
public interface IDataPoint {
int Index { get; set; }
double Value { get; set; }
DateTime TimeStampLocal { get; set; }
IDataPoint Clone();
}
public sealed class SimpleDataPoint : IDataPoint {
public int Index { get; set; }
public double Value { get; set; }
public DateTime TimeStampLocal { get; set; }
public IDataPoint Clone() {
return new SimpleDataPoint {
Index = Index,
Value = Value,
TimeStampLocal = TimeStampLocal,
};
}
}
// here's the covariant, read-only part of the interface declaration
public interface IReadOnlyDataSeries<out TDataPoint> where TDataPoint : class, IDataPoint {
object Source { get; }
int Count { get; }
double GetValue(int index);
DateTime GetTimeStampLocal(int index);
TDataPoint GetDataPoint(int index);
TDataPoint GetLastDataPoint();
}
// add a few bits to the read-write fully-typed interface, breaking covariance,
// but being able to implicitly cast to the covariant readonly version when needed
public interface IDataSeries<TDataPoint> : IReadOnlyDataSeries<TDataPoint> where TDataPoint : class, IDataPoint {
void Add(TDataPoint dataPoint);
IDataSeries<TDataPoint> Branch(object source);
}
public class DataSeries<TDataPoint> : IDataSeries<TDataPoint> where TDataPoint : class, IDataPoint {
readonly List<TDataPoint> _data = new List<TDataPoint>();
public object Source {
get;
private set;
}
public DataSeries(object source) {
Source = source;
}
public int Count {
get { return _data.Count; }
}
public TDataPoint GetDataPoint(int index) {
return _data[index];
}
public TDataPoint GetLastDataPoint() {
return _data[_data.Count - 1];
}
public DateTime GetTimeStampLocal(int index) {
return _data[index].TimeStampLocal;
}
public double GetValue(int index) {
return _data[index].Value;
}
public void Add(TDataPoint dataPoint) {
_data.Add(dataPoint);
}
public IDataSeries<TDataPoint> Branch(object source) {
throw new NotImplementedException();
}
}
}
This minimal outline of your original code demonstrates that the issue can be fixed by making TDataPoint covariant in the IDataSeries interface declaration:
using System;
namespace ConsoleApplication1
{
class Program
{
[STAThread]
static void Main(string[] args)
{
var ds = (IDataSeries<IDataPoint>)new DataSeries<SimpleDataPoint>();
Console.ReadKey();
}
}
public interface IDataPoint { }
public sealed class SimpleDataPoint : IDataPoint { }
public interface IDataSeries<out TDataPoint> where TDataPoint : class, IDataPoint { }
public class DataSeries<TDataPoint> : IDataSeries<TDataPoint> where TDataPoint : class, IDataPoint { }
}
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
I would like to create a method that can return the generic type defined in the class, here is a detailed example;
https://dotnetfiddle.net/SApVp3
using System;
public class Program
{
public static void Main()
{
// This would be some string imported from a CSV file
var customerData = "Customer,1,Ford";
var personData = "Person,675,Henry,Ford";
var customerImporter = new ImportData<CompanyMaster>();
customerImporter.ImportDataFromFile(customerData);
var personImporter = new ImportData<PersonMaster>();
personImporter.ImportDataFromFile(personData);
}
}
public class GenericRepository<TBase>
where TBase : EntityBase
{
public void Insert(TBase entity)
{
//.. generic Insert to database
}
}
public class ImportData<TBase>
where TBase : EntityBase
{
GenericRepository<TBase> _genericRepository;
//ctor
public void ImportDataFromFile(string data)
{
// convert the string data to TBase
_genericRepository = new GenericRepository<TBase>();
}
}
public class CsvConverter<TBase> where TBase: EntityBase{
public TBase ConvertTo(string someString)
{
if (someString.StartsWith("Customer"))
{
return GetCompany(someString);
}
else return GetPerson(someString);
}
private CompanyMaster GetCompany(string companyString){
return new CompanyMaster();
}
private PersonMaster GetPerson(string companyString){
return new PersonMaster();
}
}
public abstract class EntityBase
{
public int Id { get; set; }
public DateTime CreatedDate { get; set; }
}
public class CompanyMaster : EntityBase
{
public string CompanyName { get; set; }
}
public class PersonMaster : EntityBase
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
This currently throws;
Compilation error (line 47, col 11): Cannot implicitly convert type 'CompanyMaster' to 'TBase'
Compilation error (line 50, col 15): Cannot implicitly convert type 'PersonMaster' to 'TBase'
Can this be made to work?
You need to do an upcast using:
public TBase ConvertTo(string someString)
{
if ( someString.StartsWith("Customer") )
{
return (TBase)Convert.ChangeType(GetCompany(someString), typeof(TBase));
}
else
{
return (TBase)Convert.ChangeType(GetPerson(someString), typeof(TBase));
}
}
Or as suggested by #canton7:
if ( someString.StartsWith("Customer") )
{
return (TBase)(object)GetCompany(someString);
}
else
{
return (TBase)(object)GetPerson(someString);
}
Difference between casting and using the Convert.To() method
I would like to get all of the properties contained in a class whose types inherit from a certain abstract and generic class.
public abstract class foo<T> { }
public class fooInt_Indexed : foo<int> { }
public class fooInt_Not_Indexed : foo<int> { }
public class fooString_Compressed : foo<string> { }
public class fooString_Indexed : foo<string> { }
public class fooFloat : foo<float> { }
public abstract class bar
{
}
public class foobar : bar
{
public fooInt_Indexed value { get; set; }
public fooInt_Not_Indexed someOtherValue { get; set; }
public fooFloat someFloat { get; set; }
public otherData<int> {get; set; }
}
public class barChecker<T> where T : bar
{
public List<PropertyInfo> fooprops = new List<PropertyInfo>();
public static barChecker<T> Generator()
{
var #new = new barChecker<T>();
foreach (var item in typeof(T).GetProperties())
{
if (item.PropertyType is somesortof(foo<>)) #new.fooprops.Add(item);
}
return #new;
}
What do I need to put inside the barChecker<T> class code to make its fooprops list contain the property infos of "value","someOtherValue" and "someFloat" when generated as a barChecker<foobar> ?
Here's an extension method to System.Type that will answer this and similar questions about inheritance:
public static class TypeExtensions
{
public static bool InheritsFrom(this Type t, Type baseType)
{
if (t.BaseType == null)
{
return false;
}
else if (t == baseType)
{
return true;
}
else if (t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition().InheritsFrom(baseType))
{
return true;
}
else if (t.BaseType.InheritsFrom(baseType))
{
return true;
}
return false;
}
public static bool InheritsFrom<TBaseType>(this Type t)
=> t.InheritsFrom(typeof(TBaseType));
}
This here:
item.PropertyType is somesortof(foo<>)
Has to be replaced with
typeof(YourType).IsAssignableFrom(item.PropertyType)
The 'is' operator is only for real object instances, not if you already have a Type-Reference.
So in your case 'YourType' is typeof(barchecker< foobar >) ?
I have an interface like this
public interface IPerson { }
And implementations
public class Fireman : IPerson
{
public string Name { get; set; }
public bool WithAssignedTruck { get; set; }
...
}
public class Pilot : IPerson
{
public string Name { get; set; }
public int Age { get; set; }
...
}
And pass them to a constructor
public class Registration : IRegistration
{
private readonly Fireman _fireman;
private readonly Pilot _pilot;
public Registration(Pilot pilot, Fireman fireman)
{
this._fireman = fireman;
this._pilot = pilot;
}
}
And here's what the initialization method looks like.
public T PopulateProfile<T>() where T : IPerson, new()
{
var personProfile = Activator.CreateInstance<T>();
...
return personProfile;
}
Please take note that this code is just an example.
I have a method that will set the value of each property of these classes which are from database. What I need to do is that, when I ask Ninject for any class that implements IPerson interface, Ninject should execute the method first, thus, Ninject will return an initialized class. Hope you could give me a hand. Thank you.
You can use Ninject.Extensions.Conventions in combination with an IBindingGenerator which generates a ToMethod binding:
BindingGenerator
internal class PersonBindingGenerator : IBindingGenerator
{
private static readonly MethodInfo PopulateOpenGenericMethodInfo =
typeof(IProfileService).GetMethod("PopulateProfile");
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(
Type type,
IBindingRoot bindingRoot)
{
yield return bindingRoot
.Bind(type)
.ToMethod(x => CreatePerson(x.Kernel.Get<IProfileService>(), type));
}
private static object CreatePerson(
IProfileService profileService,
Type type)
{
var closedGeneric = PopulateOpenGenericMethodInfo.MakeGenericMethod(type);
return closedGeneric.Invoke(profileService, new object[0]);
}
}
Bindings
kernel.Bind<IProfileService>().To<ProfileService>();
kernel.Bind(s => s
.FromThisAssembly()
.IncludingNonePublicTypes()
.SelectAllClasses()
.InheritedFrom<IPerson>()
.BindWith<PersonBindingGenerator>());
Test
Complete Test code for reference.
using FluentAssertions;
using Ninject;
using Ninject.Extensions.Conventions;
using Ninject.Extensions.Conventions.BindingGenerators;
using Ninject.Syntax;
using System;
using System.Collections.Generic;
using System.Reflection;
using Xunit;
namespace NinjectTest.SO36424126
{
public interface IPerson
{
string SomeValue { get; set; }
}
class BarPerson : IPerson
{
public string SomeValue { get; set; }
}
class FooPerson : IPerson
{
public string SomeValue { get; set; }
}
public interface IProfileService
{
T PopulateProfile<T>()
where T : IPerson, new();
}
internal class ProfileService : IProfileService
{
public T PopulateProfile<T>()
where T : IPerson, new()
{
var personProfile = Activator.CreateInstance<T>();
personProfile.SomeValue = "initialized";
return personProfile;
}
}
internal class PersonBindingGenerator : IBindingGenerator
{
private static readonly MethodInfo PopulateOpenGenericMethodInfo = typeof(IProfileService).GetMethod("PopulateProfile");
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
yield return bindingRoot
.Bind(type)
.ToMethod(x => CreatePerson(x.Kernel.Get<IProfileService>(), type));
}
private static object CreatePerson(IProfileService profileService, Type type)
{
var closedGeneric = PopulateOpenGenericMethodInfo.MakeGenericMethod(type);
return closedGeneric.Invoke(profileService, new object[0]);
}
}
public class Test
{
[Fact]
public void Foo()
{
var kernel = new StandardKernel();
kernel.Bind<IProfileService>().To<ProfileService>();
kernel.Bind(s => s
.FromThisAssembly()
.IncludingNonePublicTypes()
.SelectAllClasses()
.InheritedFrom<IPerson>()
.BindWith<PersonBindingGenerator>());
kernel.Get<BarPerson>().SomeValue.Should().Be("initialized");
}
}
}
I am setting a property of base class from derived class as following:
public abstract class Coverter
{
public Mydata data { get; set; }
public abstract void Convert();
}
public class Mydata
{
public int i;
}
public class Coverter1 : Coverter
{
public Coverter1(Mydata data1)
{
data = data1;
}
public override void Convert()
{
Console.WriteLine(data.i.ToString());
}
}
private static void Main(string[] args)
{
Mydata data = new Mydata();
data.i = 5;
Coverter c = new Coverter1(data);
c.Convert();
Console.ReadLine();
}
Is there any flaw with this kind of implementation ? What could be the better approach?
I can do the same thing in the following approach.
public abstract class Coverter
{
public Mydata data { get; set; }
public abstract void Convert();
}
public class Mydata
{
public int i;
}
public class Coverter1:Coverter
{
override public void Convert()
{
Console.WriteLine(data.i.ToString());
}
}
static void Main(string[] args)
{
Mydata data1 = new Mydata();
data1.i = 5;
Coverter c = new Coverter1();
c.data = data1;
c.Convert();
Console.ReadLine();
}
Which appraoch is better?
Pass the field down in the constructor. (Note: normally you should name fields starting with a lower case character and properties with uppercase). Here is an example where I fixed the naming.
public abstract class Converter
{
private readonly MyData data;
protected Converter(MyData data)
{
this.data = data;
}
public MyData Data { get { return data; } }
}
public class MyData
{
private readonly int value;
public MyData(int value)
{
this.value = value;
}
public int MyValue { get { return value; } }
}
public class Converter1 : Converter
{
public Converter1()
: base(new MyData(5))
{
}
}
I recommend the practice of using readonly fields and getters only for properties to start with. Doing so will make your types immutable which usually helps get your program correct initially. Start off immutable and then introduce mutability where you need it, and only once you need it. Having the types immutable like this requires passing the values through the constructor.