C# multiple generic in inheritance - c#

How it can be fixed? Why I can not to use this constructions?
using System;
public class Program
{
public interface IReadParamModel{ }
public interface IReadResultModel{ }
public interface IWriteParamModel{ }
public interface IWriteResultModel{ }
public interface IDataReader<TParam, TResult> where TParam : IReadParamModel where TResult : IReadResultModel
{
TResult Get(TParam param);
}
public interface IDataWriter<TParam, TResult> where TParam : IWriteParamModel where TResult : IWriteResultModel
{
TResult Write(TParam param);
}
public abstract class BaseReportService<TReader, TWriter>
where TReader : IDataReader<IReadParamModel, IReadResultModel>
where TWriter : IDataWriter<IWriteParamModel, IWriteResultModel>
{
TWriter writer;
TReader reader;
}
public class ReaderParamModel : IReadParamModel { }
public class ReadResultModel : IReadResultModel { }
public class WriteParamModel : IWriteParamModel { }
public class WriteResultModel : IWriteResultModel { }
public class DataReader : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public class DataWriter : IDataWriter<WriteParamModel, IWriteResultModel>
{
public IWriteResultModel Write(WriteParamModel param){ return null; }
}
public class ReportService : BaseReportService<DataReader, DataWriter>
{
}
}
Compilation error (line 46, col 15): The type 'Program.DataReader' cannot be used as type parameter 'TReader' in the generic type or method 'Program.BaseReportService'. There is no implicit reference conversion from 'Program.DataReader' to 'Program.IDataReader'.
Compilation error (line 46, col 15): The type 'Program.DataWriter' cannot be used as type parameter 'TWriter' in the generic type or method 'Program.BaseReportService'. There is no implicit reference conversion from 'Program.DataWriter' to 'Program.IDataWriter'.

The problem is, that IDataReader<IReadParamModel, IReadResultModel> and IDataReader<ReaderParamModel, ReadResultModel> are incompatible types. In order to make them compatible, co-/contravariance would be needed, but with TResult Get(TParam param);, TParam would be contravariant and TResult would be covariant. This means, there is no way to make the two interfaces compatible with their current usage.
The choices are, to use the interfaces directly if access to the implementation properties is not required or to use the concrete types as additional generic parameters. The following code contains three sections that demonstrate the different designs, based on the co-/contravariant IDataReader interface.
The code is restricted to the Reader part, since the examples for reader and writer are quite similar. The Test method is used to highlight some differences in the actually available types at different inheritance levels.
public interface IReadParamModel { }
public interface IReadResultModel { }
public class ReaderParamModel : IReadParamModel { }
public class ReadResultModel : IReadResultModel { }
public interface IDataReader<in TParam, out TResult>
where TParam : IReadParamModel
where TResult : IReadResultModel
{
TResult Get(TParam param);
}
// First variant - much interface usage
public class DataReader_1 : IDataReader<IReadParamModel, ReadResultModel>
{
public ReadResultModel Get(IReadParamModel param) { return null; }
}
public abstract class BaseReportService_1<TReader>
where TReader : IDataReader<IReadParamModel, IReadResultModel>
{
protected TReader reader;
// input is interface, reader.Get result is interface
protected virtual IReadResultModel Test(IReadParamModel param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_1 : BaseReportService_1<DataReader_1>
{
// input is interface, reader.Get result is concrete class
protected override IReadResultModel Test(IReadParamModel param)
{
var result = reader.Get(param);
return result;
}
}
// Second variant - less interface usage, more generic parameters
public class DataReader_2 : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public abstract class BaseReportService_2<TReader, TReaderParam>
where TReader : IDataReader<TReaderParam, IReadResultModel>
where TReaderParam : IReadParamModel
{
protected TReader reader;
// input is concrete class, reader.Get result is interface
protected virtual IReadResultModel Test(TReaderParam param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_2 : BaseReportService_2<DataReader_2, ReaderParamModel>
{
// input is concrete class, reader.Get result is concrete class
protected override IReadResultModel Test(ReaderParamModel param)
{
var result = reader.Get(param);
return result;
}
}
// Third variant - fully parameterized
public class DataReader_3 : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public abstract class BaseReportService_3<TReader, TReaderParam, TReadResult>
where TReader : IDataReader<TReaderParam, TReadResult>
where TReaderParam : IReadParamModel
where TReadResult : IReadResultModel
{
protected TReader reader;
// input is concrete class, reader.Get result is concrete class
protected virtual TReadResult Test(TReaderParam param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_3 : BaseReportService_3<DataReader_3, ReaderParamModel, ReadResultModel>
{
// input is concrete class, reader.Get result is concrete class
protected override ReadResultModel Test(ReaderParamModel param)
{
var result = reader.Get(param);
return result;
}
}
If you need the concrete types for input and output (like in the 3rd example), you should check, if you really need to specify the reader type for the ReportService.
// Fourth variant - decoupled
// the reader is not really needed for this example...
public class DataReader_4 : IDataReader<ReaderParamModel, ReadResultModel>
{
public ReadResultModel Get(ReaderParamModel param) { return null; }
}
public abstract class BaseReportService_4<TReaderParam, TReadResult>
where TReaderParam : IReadParamModel
where TReadResult : IReadResultModel
{
// reader is interface, can be assigned from DataReader_4 or different implementations
protected IDataReader<TReaderParam, TReadResult> reader;
// input is concrete class, reader.Get result is concrete class
protected virtual TReadResult Test(TReaderParam param)
{
var result = reader.Get(param);
return result;
}
}
public class ReportService_4 : BaseReportService_4<ReaderParamModel, ReadResultModel>
{
// input is concrete class, reader.Get result is concrete class
protected override ReadResultModel Test(ReaderParamModel param)
{
var result = reader.Get(param);
return result;
}
}

Related

Using reflection to instantiate an IEnumerable<MyType> where MyType has a generic parameter

I've built a simple extensible computation framework, where each class represents a different function for the framework.
This is a quick example of what I did:
BaseFunction:
namespace MyNamespace
{
public abstract class BaseFunction
{
public abstract string Name { get; }
public abstract int Index { get; }
public long Execute()
{
Execute(ReadInput() /* out of scope */, out long result);
return result;
}
internal abstract void Execute(string input, out long rResult);
}
}
SampleFunction:
namespace MyNamespace.Code
{
public class SampleFunction: BaseFunction
{
public override string Name => "Sample Function";
public override int Index => 1;
internal override void Execute(string input, out long result)
{
result = 0;
}
}
}
Using reflection, the framework also provided a CLI where the user can select its function and run it.
This is how all the functions are retrieved:
public static IEnumerable<BaseFunction> Functions()
{
return GetTypesInNamespace(Assembly.GetExecutingAssembly(), "MyNamespace.Code")
.Where(type => type.Name != "BaseFunction")
.Select(type => (BaseFunction)Activator.CreateInstance(type))
.OrderBy(type => type.Index);
}
and this is how the CLI is built:
var menu = new EasyConsole.Menu();
foreach (var day in FunctionsUtils.Functions())
{
menu.Add(function.Name, () => function.Execute());
}
The framework works fine, but, as you can see, everything is a long now, and this takes us to my issue: I'd like to make the BaseFunction class generic, so that I can have different functions returning different type of values.
However, changing BaseFunction to BaseFunction<TResult> breaks the Functions method as I can't return a IEnumerable<BaseFunction>.
The logical next step is to add an interface, make BaseFunction implement the interface and add the generics to BaseFunction. This means that Functions can now return a IEnumerable<IBaseFunction>.
What still doesn't work, however, is the way I build the CLI menu: my interface must have the Execute method, and so we're back to square one: I can't add that method to my interface, because the return type is a generic and the interface doesn't have the generic reference.
Here I'm kind of stuck.
Is there any way to make this kind of framework work without changing all my return types to object (or maybe struct?) considering that I may also need to return non-numeric types?
Assuming that input and result can be anything, you need something like this:
public abstract class BaseFunction
{
public abstract string Name { get; }
public abstract int Index { get; }
public object Execute() => Execute(ReadInput());
private object ReadInput()
{
// out of scope
return null;
}
protected abstract object Execute(object input);
}
public abstract class BaseFunction<TInput, TResult> : BaseFunction
{
protected sealed override object Execute(object input) => Execute(ConvertInput(input));
protected abstract TInput ConvertInput(object input);
protected abstract TResult Execute(TInput input);
}
public sealed class SampleFunction : BaseFunction<string, long>
{
public override string Name => "Returns string length";
public override int Index => 0;
protected override string ConvertInput(object input) => (string)input;
protected override long Execute(string input) => input.Length;
}
This still allows you to combine functions into IEnumerable<BaseFunction>, execute them, but also allows to work with strongly-typed input and result, when implementing particular function.
(I've modified BaseFunction a little to throw away out parameter)
If you change BaseFunction to BaseFunction<TResult>
public abstract class BaseFunction<TResult>
Why not then just change the signature of Functions to return BaseFunction <TResult> ?
public static class FunctionClass<TResult>
{
public static IEnumerable<BaseFunction<TResult>> Functions()
{
Update:
Extracting a base interface to find commonality, then setting up TResult in the abstract class seems to work. I also refined the linq query a bit.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace MyNamespace
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var list = FunctionClass.Functions();
foreach(var item in list)
{
Console.WriteLine($"{item.Name} - {item.GetType().Name}");
if(item is BaseFunction<int>)
{
Console.WriteLine($"int result {((BaseFunction<int>)item).Execute()}");
}
if (item is BaseFunction<long>)
{
Console.WriteLine($"long result {((BaseFunction<long>)item).Execute()}");
}
}
Console.WriteLine("\n\nPress Any Key to Close");
Console.ReadKey();
}
}
public class FunctionClass
{
private static Type[] GetTypesInNamespace(Assembly assembly, string nameSpace)
{
return
assembly.GetTypes()
.Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal))
.ToArray();
}
public static IEnumerable<IBaseFunction> Functions()
{
return GetTypesInNamespace(Assembly.GetExecutingAssembly(), "MyNamespace.Code")
.Where(type => type.IsClass && typeof(IBaseFunction).IsAssignableFrom(type))
.Select(type => (IBaseFunction)Activator.CreateInstance(type))
.OrderBy(type => ((IBaseFunction)type).Index);
}
}
}
namespace MyNamespace
{
public interface IBaseFunction
{
public string Name { get; }
public long Index { get; }
}
public abstract class BaseFunction<TResult> : IBaseFunction
{
public virtual string Name { get; }
public virtual long Index { get; }
public TResult Execute()
{
Execute("foo" /* out of scope */, out TResult result);
return result;
}
internal abstract void Execute(string input, out TResult rResult);
}
}
namespace MyNamespace.Code
{
public class SampleFunction : BaseFunction<int>
{
public override string Name => "Sample Function1 - with int";
public override long Index => 1;
internal override void Execute(string input, out int rResult)
{
rResult = 0;
}
}
public class SampleFunction2 : BaseFunction<long>
{
public override string Name => "Sample Function2 - with long";
public override long Index => 1;
internal override void Execute(string input, out long result)
{
result = 0;
}
}
}

Check if generic parameter of a class implements an interface

I have a following generic class:
public class SearchResult<T>
{
public int ResultCount { get; set; }
public IEnumerable<T> Result { get; set; }
}
I also have a Bird class, which implements IFlyble interface:
public class Bird : IFlyable
{
public void Fly() {}
}
public interface IFlyable
{
void Fly();
}
I also have a variable res of type object.
How do I check if res is a SearchResult<IFlyable> ?
I tryied this way:
if (res.GetType().IsAssignableFrom(typeof(SearchResult<IFlyable>)))
{
///
}
And this way:
if(res is SearchResult<IFlyable>)
{
///
}
But it does not seems to work.
The problem you are having is probably due to the fact that SearchResult<Bird> is not convertible to SearchResult<IFlyable> because SearchResult<T> is invariant in T.
C# only admitís generic type variance in interfaces and delegates. You need to define an ISearchResult<> interface that is covariant in its generic type.
In your case, if it’s accepatable that T Is only used as an output you could define such interface as follows:
public interface ISearchResult<out T>
{
int ResultCount { get; }
IEnumerable<T> Result { get; }
}
And now a ISearchResult<Bird> is a ISearchResult<IFlyable> because you’ve given the compiler enough information so that it can verify that the conversion is in fact safe
You can also try this using reflection, which also works and no need to create another interface.
static void Main()
{
var sr = new SearchResult<Bird>();
Console.WriteLine(IsSearchResultIFlyable(sr.GetType())
? "sr is SearchResult<IFlyable>"
: "sr is Not SearchResult<IFlyable>");
Console.ReadLine();
}
public static bool IsSearchResultIFlyable(Type t)
{
if (!t.IsGenericType) return false;
if (t.GetGenericTypeDefinition() != typeof(SearchResult<>)) return false;
var gr = t.GetGenericArguments();
return gr.Length == 1 && typeof(IFlyable).IsAssignableFrom(gr[0]);
}

The type 'XXX' cannot be used as type parameter 'T' in the generic type or method

I have the following classes
public interface IHtmlString
{
string ToHtmlString();
}
public class BaseClass<T> where T : BaseClass<T>, IHtmlString
{
public BaseClass()
{
}
public T Method1()
{
return (T)this;
}
public T Method2(string key, object value)
{
return (T)this;
}
public string ToHtmlString()
{
return ToString();
}
}
public class DerivedClassA : BaseClass<DerivedClassA>
{
public DerivedClassA MethodSpecificToClassA()
{
return this;
}
public override string ToString()
{
return "<div/>";
}
}
public class DerivedClassB : BaseClass<DerivedClassB>
{
public DerivedClassB MethodSpecificToClassB()
{
return this;
}
public override string ToString()
{
return "<span/>";
}
}
Note that the method returns this instance so that i can chain methods, like below
var a = new DerivedClassA()
.Method1()
.MethodSpecificToClassA()
.ToHtmlString();
However im getting compile time error
Severity Code Description Project File Line Suppression State
Error CS0311 The type 'DerivedClassA' cannot be
used as type parameter 'T' in the generic type or method
'BaseClass'. There is no implicit reference conversion from
'DerivedClassA' to
'System.Web.IHtmlString'. WebApplication5 C:\MyProjects\WebApplication5\WebApplication5\Models\BaseBuilder.cs 27 Active
How do i refactor these classes so i have IHtmlString interface on base class only?
this worked
public class BaseClass<T> : IHtmlString where T : BaseClass<T>
{
}

C# Get type of subClass and create object of this

class MainClass {
private int someMethod() {
IList <SubClass> obj = db.Query <SubClass> (delegate(SubClass obj) {
return obj.Points == 100;
});
}
}
class SubClass : MainClass {
public int someField;
}
SubClass obj = new SubClass();
obj.someMethod();
SubClasses can be differnt, i need get instance of this for make a query to db4o.
Evaluating subclass types from base class to make some processing, is generally a bad decision, consider adding a composed method to the base class, then leverage specialized processing to its subclass:
class MainClass {
public string ComposedMethod(){
//Base class processing...
var retVal = someMethod();
//Even more Base class processing...
return retVal;
}
public virtual string someMethod(){
}
}
class SubClass : MainClass {
public override string someMethod(){
return this.GetType().ToString(); //Or whatever you need to do
}
}
Usage:
SubClass obj = new SubClass();
obj.ComposedMethod();

How to write an interface for a generic method

I have the class PlayersCollection and I want to interface it in IWorldCollection.
The probleme is about writing the declaration in the interface which cause me this error :
Assets/Scripts/Arcane/api/Collections/ItemsCollection.cs(17,22): error CS0425:
The constraints for type parameter `T' of method
`Arcane.api.ItemsCollection.Get<T>(int)
must match the constraints for type parameter `T' of
interface method `Arcane.api.IWorldCollection.Get<T>(int)'.
Consider using an explicit interface implementation instead
Here is my class and my interface. How to write a generic method implementation with a class constraint ?
public class PlayersCollection : IWorldCollection
{
public Dictionary<Type, object> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, object>();
}
public T Get<T>(int id) where T: PlayerModel
{
var t = typeof(T);
if (!Collection.ContainsKey(t)) return null;
var dict = Collection[t] as Dictionary<int, T>;
if (!dict.ContainsKey(id)) return null;
return (T)dict[id];
}
}
}
public interface IWorldCollection
{
T Get<T>(int id) where T : class;// Work when I change "class" to "PlayerModel".
}
Big thanks for your help :)
It seems to me that the following will meet the requirements, by pushing the generic type parameter up to the class/interface level:
public class PlayersCollection<T> : IWorldCollection<T> where T : PlayerModel
{
public Dictionary<Type, T> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, T>();
}
public T Get(int id)
{
var t = typeof(T);
if (!Collection.ContainsKey(t)) return null;
var dict = Collection[t] as Dictionary<int, T>;
if (!dict.ContainsKey(id)) return null;
return (T)dict[id];
}
}
public interface IWorldCollection<T> where T : class
{
T Get(int id);
}
If I have missed something in the requirements, please let me know.
I'm not sure why you need this interface, but maybe this will help:
public class PlayersCollection<T> : IWorldCollection<T> where T:PlayerModel
{
public Dictionary<Type, object> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, object>();
}
public T Get(int id)
{
...
}
}
public interface IWorldCollection<T> where T:class
{
T Get(int id);
}
Try this:
public class PlayersCollection : IWorldCollection<PlayerModel>
{
public Dictionary<Type, object> Collection;
public PlayersCollection()
{
Collection = new Dictionary<Type, object>();
}
public PlayerModel Get<PlayerModel>(int id)
{
///
}
}
}
public interface IWorldCollection<T>
{
T Get<T>(int id);
}
In your case, in the class implementing your interface, you adding more condition for your class:
where T : class in the interface
where T: PlayerModel in the class
If, for some reason, you need to add a constraint into your interface, you need to add an interface or base class, which will be placed to the interface declaration, and you'll have to derive from it in your PlayerModel class, like this:
public abstract class Model
{
}
public class PlayerModel : Model
{
}
public interface IWorldCollection<T> where T : Model
{
T Get<T>(int id);
}
public class PlayersCollection : IWorldCollection<PlayerModel>
{
///
public PlayerModel Get<PlayerModel>(int id)
{
///
}
}
}

Categories

Resources