C# nested generic constraint (syntactic sugar) [duplicate] - c#

I have a generic method like this (simplified version):
public static TResult PartialInference<T, TResult>(Func<T, TResult> action, object param)
{
return action((T)param);
}
In the above, param is of type object on purpose. This is part of the requirement.
When I fill in the types, I can call it like this:
var test1 = PartialInference<string, bool>(
p => p.EndsWith("!"), "Hello world!"
);
However, I'd like to use type inference. Preferably, I would like to write this:
var test2 = PartialInference<string>(
p => p.EndsWith("!"), "Hello world!"
);
But this does not compile. The best I came up with is this:
var test3 = PartialInference(
(string p) => p.EndsWith("!"), "Hello world!"
);
The reason I would like to have this as a type parameter and still have the correctly typed return type is because my actual calls look something like this:
var list1 = ComponentProvider.Perform(
(ITruckSchedule_StaffRepository p) => p.GetAllForTruckSchedule(this)
)
Which is very ugly and I would love to write as something like this:
var list2 = ComponentProvider.Perform<ITruckSchedule_StaffRepository>(
p => p.GetAllForTruckSchedule(this)
)

You can split t into a generic method on a generic type:
class Foo<TOuter> {
public static void Bar<TInner>(TInner arg) {...}
}
...
int x = 1;
Foo<string>.Bar(x);
Here the int is inferred but the string is explicit.

What you are trying to achieve is not possible. You need to specify both generic arguments or none of the them if inference is possible.

You can use reflection... like this below
Here is an example of how to call a extension method with two generic parameters.
We have to ways to execute the extension method:
a) Directly from an abstract base class
b) From an instance object that derived from that base class
Not mandatory to implement like so, but I found it very handy.
a) You must supply the two generic arguments as usual.
b) You already have one of the generic types since you are using an instance. The other generic parameter must by passed as type argument, you cannot pass it a second generic parameter due to ambiguity.
(see How to pass 2 generics types into an extension method)
public interface IEntityDto
{
// Not relevant to this example, how is defined , is just an interface, it could be removed, if your generic types don't need interface constraints
}
public interface IRowVersion
{
// Not relevant to this example, how is defined , is just an interface, it could be removed, if your generic types don't need interface constraints
}
public interface IPropertyMappingValue
{
// Not relevant to this example, how is defined , is just an interface, it could be removed, if your returned object don't need interface constraints
string Value { get; set; }
}
public class PropertyMappingValue : IPropertyMappingValue
{
// Not relevant to this example, how is defined , is just an object, returned by our extension method
public string Value { get; set; }
}
public abstract class EntityBase
{
public static IPropertyMappingValue GetPropertyMappingValue<TEntity, TEntityDto>(string name) where TEntity : class, IRowVersion where TEntityDto : class, IEntityDto => EntityExtensions.GetPropertyMappingValue<TEntity, TEntityDto>(name);
}
// Sample Class
public class Entity : IRowVersion
{
}
// Sample Class
public class EntityDto : EntityBase, IEntityDto
{
}
public static class EntityExtensions
{
public static IPropertyMappingValue GetPropertyMappingValue<TEntityDto>(this TEntityDto instance, Type entityType, string name) where TEntityDto : class, IEntityDto
{
if (!typeof(IRowVersion).IsAssignableFrom(entityType))
throw new ArgumentException($"{entityType} do not implements {typeof(IRowVersion)}");
var method = typeof(EntityExtensions).GetMethod(nameof(GetPropertyMappingValue), new[] { typeof(string) });
var typeArgs = new[] { entityType, typeof(TEntityDto) };
var constructed = method?.MakeGenericMethod(typeArgs);
var result = constructed?.Invoke(null, new object[] { name });
return result as IPropertyMappingValue;
}
public static IPropertyMappingValue GetPropertyMappingValue<TEntity, TEntityDto>(string name) where TEntity : class, IRowVersion where TEntityDto : class, IEntityDto
{
//TO DO YOUR JOB HERE TO GET A VALID RETURNED OBJECT, as this is an example I will return a fake
// THE CODE IS JUST AN EXAMPLE of doing something with the types, but is not relevant for this example
//
var foo = typeof(TEntityDto);
var bar = typeof(TEntity);
//
return new PropertyMappingValue { Value = name }; // returning just a fake object
}
}
public class UnitTest
{
private readonly ITestOutputHelper _console;
public UnitTest(ITestOutputHelper console)
{
_console = console;
}
[Fact]
public void Test()
{
var oneWayOfExecuting = EntityBase.GetPropertyMappingValue<Entity, EntityDto>("Hello world"); //using a abstract base
_console.WriteLine(oneWayOfExecuting.Value);
var entityDto = new EntityDto();
var anotherWayOfExecuting = entityDto.GetPropertyMappingValue(typeof(Entity), "Hello world"); //using the extension method
_console.WriteLine(anotherWayOfExecuting.Value);
Assert.Equal("Hello world", oneWayOfExecuting.Value);
Assert.Equal("Hello world", oneWayOfExecuting.Value);
}

Related

Why my convarian (out keyword) not working?

How to use the Covariance(out keyword)?I have no idea.
I know out keyword in Interface mean Covariance that allow you use a more derived type than that specified by the generic parameter.
So i set generic to object and return type to string.Because string is subclass of object.
But i tried and it not working.
public interface IMyInterface<out T>
{
public T Foo();
}
public class CovarianceTest : IMyInterface<object>
{
public string Foo()
{
return "abc";
}
}
Covariance that allow you use a more derived type than that specified by the generic parameter
That's not what it means. You still have to write methods that match the types in the interface. So you will have to do this:
public class CovarianceTest : IMyInterface<string>
{
public string Foo()
{
return "abc";
}
}
What covariance allow you to do is assign the class instance to a variable typed as a less specific interface, like this:
IMyInterface<object> x = new CovarianceTest();
Console.WriteLine(x.Foo());
That's because all Ts read from the interface (i.e. taken out) are guaranteed to be instances that are type-compatible with the less specific type, i.e. all strings are also objects.
I suspect you are trying to do the following.
public interface IMyInterface<out T>
{
public T Foo();
}
public class CovarianceTest : IMyInterface<string>
{
public string Foo()
{
return "abc";
}
}
IMyInterface<string> works = new CovarianceTest();
IMyInterface<object> alsoWorks = new CovarianceTest();
IMyInterface<int> doesNotWork = new CovarianceTest();

C# subclassing with generics: I need an extra generic parameter for ctor, but how?

I have a class
public class LDBList<T> : List<T> where T : LDBRootClass {
// typical constructor
public LDBList(LDBList<T> x) : base(x) { }
...
}
but I want to have an extra constructor that takes a list of a different generic type (say A), and a function that converts an A to a T, and build the T list from that, something like
public LDBList(
Func<A, T> converter,
IList<A> aList)
{
foreach (var x in aList) {
this.Append(converter(x));
}
}
so converter is of type A->T so I take an A list and make a T list from it. My class is parameterised by T so that's fine.
But it's complaining "The type or namespace name 'A' could not be found".
OK, so it needs the an A generic parameter on the class I suppose (it really doesn't like it on the constructor). But where do I put it, in fact is this even possible?
I don't believe you can add additional generic types to a constructor like that that.
I would refactor the converter to do the creation and return the instance of LDBList, that way the convert acts as a factory for creating LDBLists from instances of A.
public class Converter<T,A>
{
public LDbList<T> CreateLdbList(IList<A>) {
var list = new LdbList<T>();
// do the conversion here
return list;
}
}
then, change the usage to be
var Converter<X,Y> = new Converter();
var result = Converter.Convert(originalData);
In theory you could add the extra type to the generic types for the class itself:
public class LDBList<A, T> : List<T> where T : LDBRootClass
{
// typical constructor
public LDBList(LDBList<A, T> x) : base(x) { }
public LDBList(
Func<A, T> converter,
IList<A> aList)
{
foreach (var x in aList)
{
this.Append(converter(x));
}
}
}
But of course that means that you need to declare the instances of that type with an extra type parameter that you won't even need unless you're using that specific constructor. So that's no good.
You could declare a helper class like so:
public static class LDBListCreator
{
public static LDBList<T> CreateFrom<T, A>(Func<A, T> converter, IList<A> aList) where T: LDBRootClass
{
var result = new LDBList<T>();
result.AddRange(aList.Select(converter));
return result;
}
}
This assumes that LDBList<T> has a default constructor.
But on inspection, you should be able to see that it's pointless creating such a simple helper class. If you add a constructor to your list class that accepts an IEnumerable<T> (like the List<T> class has) like so:
public LDBList(IEnumerable<T> items) : base(items)
{
}
Then you can construct an instance of LDBList<T> just by using IEnumerable.Select().
For example, given:
public class LDBRootClass
{
}
public class DerivedLDBRootClass : LDBRootClass
{
public DerivedLDBRootClass(string value)
{
// .. whatever
}
}
Then you could convert from a List<string> to a LDBList<DerivedLDBRootClass> very simply without any additional scaffolding, like so:
var strings = new List<string> { "One", "Two", "Three", "Four", "Five" };
var result = new LDBList<DerivedLDBRootClass>(strings.Select(item => new DerivedLDBRootClass(item)));

Creating a generic interface or class to add instance constructors to a generic model [duplicate]

In C# you can put a constraint on a generic method like:
public class A {
public static void Method<T> (T a) where T : new() {
//...do something...
}
}
Where you specify that T should have a constructor that requires no parameters. I'm wondering whether there is a way to add a constraint like "there exists a constructor with a float[,] parameter?"
The following code doesn't compile:
public class A {
public static void Method<T> (T a) where T : new(float[,] u) {
//...do something...
}
}
A workaround is also useful?
As you've found, you can't do this.
As a workaround I normally supply a delegate that can create objects of type T:
public class A {
public static void Method<T> (T a, Func<float[,], T> creator) {
//...do something...
}
}
Using reflection to create a generic object, the type still needs the correct constructor declared or an exception will be thrown. You can pass in any argument as long as they match one of the constructors.
Used this way you cannot put a constraint on the constructor in the template.
If the constructor is missing, an exception needs to be handled at run-time rather than getting an error at compile time.
// public static object CreateInstance(Type type, params object[] args);
// Example 1
T t = (T)Activator.CreateInstance(typeof(T));
// Example 2
T t = (T)Activator.CreateInstance(typeof(T), arg0, arg1, arg2, ...);
// Example 3
T t = (T)Activator.CreateInstance(typeof(T), (string)arg0, (int)arg1, (bool)arg2);
There is no such construct. You can only specify an empty constructor constraint.
I work around this problem with lambda methods.
public static void Method<T>(Func<int,T> del) {
var t = del(42);
}
Use Case
Method(x => new Foo(x));
Here is a workaround for this that I personally find quite effective. If you think of what a generic parameterized constructor constraint is, it's really a mapping between types and constructors with a particular signature. You can create your own such mapping by using a dictionary. Put these in a static "factory" class and you can create objects of varying type without having to worry about building a constructor lambda every time:
public static class BaseTypeFactory
{
private delegate BaseType BaseTypeConstructor(int pParam1, int pParam2);
private static readonly Dictionary<Type, BaseTypeConstructor>
mTypeConstructors = new Dictionary<Type, BaseTypeConstructor>
{
{ typeof(Object1), (pParam1, pParam2) => new Object1(pParam1, pParam2) },
{ typeof(Object2), (pParam1, pParam2) => new Object2(pParam1, pParam2) },
{ typeof(Object3), (pParam1, pParam2) => new Object3(pParam1, pParam2) }
};
then in your generic method, for example:
public static T BuildBaseType<T>(...)
where T : BaseType
{
...
T myObject = (T)mTypeConstructors[typeof(T)](value1, value2);
...
return myObject;
}
No. At the moment the only constructor constraint you can specify is for a no-arg constructor.
I think this is the most clean solution that kind of puts a constraint on the way an object is constructed. It is not entirely compile time checked. When you have the agreement to make the actual constructor of the classes have the same signature like the IConstructor interface, it is kind of like having a constraint on the constructor. The Constructor method is hidden when working normally with the object, because of the explicit interface implementation.
using System.Runtime.Serialization;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
var employeeWorker = new GenericWorker<Employee>();
employeeWorker.DoWork();
}
}
public class GenericWorker<T> where T:IConstructor
{
public void DoWork()
{
T employee = (T)FormatterServices.GetUninitializedObject(typeof(T));
employee.Constructor("John Doe", 105);
}
}
public interface IConstructor
{
void Constructor(string name, int age);
}
public class Employee : IConstructor
{
public string Name { get; private set; }
public int Age { get; private set; }
public Employee(string name, int age)
{
((IConstructor)this).Constructor(name, age);
}
void IConstructor.Constructor(string name, int age)
{
Name = name;
Age = age;
}
}
}
How about creating your generic class with constraints, here I chose struct and class to have value and reference types.
That way your constructor has a constraint on the values.
class MyGenericClass<T, X> where T :struct where X: class
{
private T genericMemberVariableT;
private X genericMemberVariableX;
public MyGenericClass(T valueT, X valueX)
{
genericMemberVariableT = valueT;
genericMemberVariableX = valueX;
}
public T genericMethod(T genericParameter)
{
Console.WriteLine("Parameter type: {0}, value: {1}", typeof(T).ToString(), genericParameter);
Console.WriteLine("Return type: {0}, value: {1}", typeof(T).ToString(), genericMemberVariableT);
Console.WriteLine("Return type: {0}, value: {1}", typeof(X).ToString(), genericMemberVariableX);
return genericMemberVariableT;
}
public T genericProperty { get; set; }
}
Implementation:
MyGenericClass<int, string> intGenericClass = new MyGenericClass<int, string>(10, "Hello world");
int val = intGenericClass.genericMethod(200);
Here's the recommended workaround by c# maintainers if you'd like to keep the constructor parameter-ful, call the constructor indirectly:
i = (TService)Activator.CreateInstance(typeof(TService), new object[] {arg});
Where TService is a generic with a parameter-full constructor that I'd like to keep.
If you'd like to read up on how this method works:
https://learn.microsoft.com/en-us/dotnet/api/system.activator.createinstance?view=net-5.0#system-activator-createinstance(system-type-system-object-)
Aaaaand discussion by maintainers of C#:
https://github.com/dotnet/csharplang/discussions/769
As alternative (from C# 9+), you can define a interface with "init" properties, as you would to arguments of a constructor.
One major benefit is it works for structs or classes.
using System;
public class Program
{
public interface ITest
{
int a { init; }
}
public struct Test : ITest{
public int a { private get; init; }
public int b => a;
}
public static T TestFunction<T>() where T: ITest, new() {
return new(){ a = 123 };
}
public static void Main()
{
var t = TestFunction<Test>();
Console.WriteLine($"Hello World: {t.b}"); // Prints: Hello World: 123
}
}

How do I properly work with calling methods on related but different classes in C#

To be honest I wasn't sure how to word this question so forgive me if the actual question isn't what you were expecting based on the title. C# is the first statically typed language I've ever programmed in and that aspect of it has been an absolute headache for me so far. I'm fairly sure I just don't have a good handle on the core ideas surrounding how to design a system in a statically typed manner.
Here's a rough idea of what I'm trying to do. Suppose I have a hierarchy of classes like so:
abstract class DataMold<T>
{
public abstract T Result { get; }
}
class TextMold : DataMold<string>
{
public string Result => "ABC";
}
class NumberMold : DataMold<int>
{
public int Result => 123
}
Now suppose I want to make a list of item where the items can be any kind of mold and I can get the Result property of each item in a foreach loop like so:
List<DataMold<T>> molds = new List<DataMold<T>>();
molds.Add(new TextMold());
molds.Add(new NumberMold());
foreach (DataMold<T> mold in molds)
Console.WriteLine(mold.Result);
As you probably already know, that doesn't work. From what I've read in my searches, it has to do with the fact that I can't declare the List to be of type DataMold<T>. What is the correct way to go about something like this?
The short answer: You can't.
One of the things that is counterintuitive about generic types is that they are not related. A List<int>, for example, has no relationship whatsoever to a List<string>. They do not inherit from each other, and you can't cast one to the other.
You can declare a covariance relationship, which looks a lot like an inheritance relationship, but not between an int and a string as you have declared, since one is a value type and one is a reference type.
Your only alternative is to add another interface that they have in common, like this:
interface IDataMold
{
}
abstract class DataMold<T> : IDataMold
{
public abstract T Result { get; }
}
Now you can store all of your molds in a List<IDataMold>. However, the interface has no properties, so you'd have a heckuva time getting anything out of it. You could add some properties, but they would not be type-specific, as IDataMold has no generic type parameter. But you could add a common property
interface IDataMold
{
string ResultString { get; }
}
...and implement it:
abstract class DataMold<T>
{
public abstract T Result { get; }
public string ResultString => Result.ToString();
}
But if your only need is to display a string equivalent for each item, you can just override ToString() instead:
class TextMold : DataMold<string>
{
public string Result => "ABC";
public override string ToString() => Result.ToString();
}
Now you can do this:
List<IDataMold> molds = new List<IDataMold>();
molds.Add(new TextMold());
molds.Add(new NumberMold());
foreach (var mold in molds)
{
Console.WriteLine(mold.ToString());
}
You're looking for covariance. See the out keyword before T generic type parameter:
// Covariance and contravariance are only possible for
// interface and delegate generic params
public interface IDataMold<out T>
{
T Result { get; }
}
abstract class DataMold<T> : IDataMold<T>
{
public abstract T Result { get; }
}
class StringMold : DataMold<string> {}
class Whatever {}
class WhateverMold : DataMold<Whatever> {}
Now inherit DataMold<T> and create a List<IDataMold<object>>:
var molds = new List<IDataMold<object>>();
molds.Add(new StringMold());
molds.Add(new WhateverMold());
BTW, you can't use covariance when it comes to cast IDataMold<int> to IDataMold<object>. Instead of repeating what's been already explained, please see this other Q&A: Why covariance and contravariance do not support value type
If you're really forced to implement IDataMold<int>, that list may be of type object:
var molds = new List<object>();
molds.add(new TextMold());
molds.add(new NumberMold());
And you may use Enumerable.OfType<T> to get subsets of molds:
var numberMolds = molds.OfType<IDataMold<int>>();
var textMolds = molds.OfType<IDataMold<string>>();
Also, you may create two lists:
var numberMolds = new List<IDataMold<int>>();
var textMolds = new List<IDataMold<string>>();
So you might mix them later as an IEnumerable<object> if you need to:
var allMolds = numberMolds.Cast<object>().Union(textMolds.Cast<object>());
You could use a visitor pattern:
Add a visitor interface that accepts all your types, and implement a visitor that performs the action you want to apply to all DataMolds:
interface IDataMoldVisitor
{ void visit(DataMold<string> dataMold);
void visit(DataMold<int> dataMold);
}
// Console.WriteLine for all
class DataMoldConsoleWriter : IDataMoldVisitor
{ public void visit(DataMold<string> dataMold)
{ Console.WriteLine(dataMold.Result);
}
public void visit(DataMold<int> dataMold)
{ Console.WriteLine(dataMold.Result);
}
}
Add an acceptor interface that your list can hold and have your DataMold classes implement it:
interface IDataMoldAcceptor
{ void accept(IDataMoldVisitor visitor);
}
abstract class DataMold<T> : IDataMoldAcceptor
{ public abstract T Result { get; }
public abstract void accept(IDataMoldVisitor visitor);
}
class TextMold : DataMold<string>
{ public string Result => "ABC";
public override void accept(IDataMoldVisitor visitor)
{ visitor.visit(this);
}
}
class NumberMold : DataMold<int>
{ public int Result => 123;
public override void accept(IDataMoldVisitor visitor)
{ visitor.visit(this);
}
}
And finally, execute it with:
// List now holds acceptors
List<IDataMoldAcceptor> molds = new List<IDataMoldAcceptor>();
molds.Add(new TextMold());
molds.Add(new NumberMold());
// Construct the action you want to perform
DataMoldConsoleWriter consoleWriter = new DataMoldConsoleWriter();
// ..and execute for each mold
foreach (IDataMoldAcceptor mold in molds)
mold.accept(consoleWriter);
Output is:
ABC
123
dynamic
This can be done with the dynamic keyword, at the cost of performance and type safety.
var molds = new List<object>(); // System.Object is the most derived common base type.
molds.Add(new TextMold());
molds.Add(new NumberMold());
foreach (dynamic mold in molds)
Console.WriteLine(mold.Result);
Now that mold is dynamic, C#'ll check what mold's type is at run-time and then figure out what .Result means from there.

Is it possible to have strongly typed parameter constraints in a constructor of a generic class?

I would like to define a generic class in C#:
public class MyService<TModel>
{
public MyService(/*What to declare here?*/)
{
}
}
such that I can instantiate it like the following:
var service = new MyService<SomeModel>(m => m.SomeField);
where SomeField is restricted to be a field of SomeModel. Is this possible?
I know I can declare something similar for a method within the class but can't quite figure out how this can be done for constructors. For example, I can declare the following in MyService:
public void SomeMethod<TMember>(Expression<Func<TModel, TMember>> member)
{
}
and be able to invoke it like this:
var service = new MyService<SomeModel>();
service.SomeMethod(m => m.SomeField);
and the compiler would complain if SomeField wasn't actually a field of SomeModel.
You can do this:
public class MyService<TModel>
{
public static MyService<TModel> Create<TMember>(Func<TModel, TMember> member)
{
return new MyServiceImpl<TMember>(member);
}
private class MyServiceImpl<TMember> : MyService<TModel>
{
private Func<TModel, TMember> member;
public MyServiceImpl(Func<TModel, TMember> member)
{
this.member = member;
}
}
}
So now I can do this:
MyService<Foo> service = MyService<Foo>.Create(m => m.Name);
Note, that I don't need to specify the type of the member.
Here's my class Foo:
public class Foo
{
public string Name;
}
You must put both TModel and TMember both in the <> in the constructor.
public class MyService<TModel, TMember>
{
public MyService(Expression<Func<TModel, TMember>> member)
{
}
}
You can't create a constructor that uses extra generic parameters that are not part of the class like you can with a method.

Categories

Resources