C# delegate variance and generics - c#

In the code below, I want to use an action having a more derived parameter pass into an action using the base as a parameter. The code looks like this:
public interface IContext<T>
{
}
public interface IWorkflow<T>
{
void Do(Action<IContext<T>> lambda);
}
public interface IDerivedContext : IContext<int>
{
}
public interface IDerivedWorkflow : IWorkflow<int>
{
void Do(Action<IDerivedContext> lambda);
}
public class Workflow<T> : IWorkflow<T>
{
public void Do(Action<IContext<T>> lambda)
{
Console.WriteLine("in Do(Action<IContext<T>>");
}
}
public class DerivedContext : IContext<int>
{
}
public class DerivedWorkflow : Workflow<int>, IDerivedWorkflow
{
public void Do(Action<IDerivedContext> lambda)
{
base.Do(lambda); // Compiler error:
}
}
If I cast this line:
base.Do(lambda);
like this:
base.Do((Action<IContext<int>>)lambda);
The compiler accepts the cast but code fails at runtime with an InvalidCastException.
Based on the MSDN documentation it suggests that the above should work because I'm passing an Action with a parameter of a more derive class to an Action using a parameter of a least derived class (in this case the base class) for example the docs illustrates the following:
static void AddToContacts(Person person)
{
// This method adds a Person object
// to a contact list.
}
static void Test()
{
// Create an instance of the delegate without using variance.
Action<Person> addPersonToContacts = AddToContacts;
// The Action delegate expects
// a method that has an Employee parameter,
// but you can assign it a method that has a Person parameter
// because Employee derives from Person.
Action<Employee> addEmployeeToContacts = AddToContacts;
}
}
Am I misunderstanding something or is there a workaround to this problem.
Thanks in advance

That's fundamentally unsafe; you can't do that.
An Action<IDerivedContext> can only take an IDerivedContext as a parameter. Had you been able to convert it to Action<IContext<int>>, you would be able to call it with some other IContext<int> implementation that it can't actually accept.

Related

Cannot cast object of T to concrete type

I'm trying to understand generic types. However, I think I'm getting confused with fakes.
On my system, I'd like to load something. It doesn't matter what it is, and it could be anything. The caller will konw what it is.
My approach is to use an interface, where the interface is simply
public interface ILoad
{
void Load<T>(T t);
}
In my Bll class, I have a method which is
public void Start(ILoad load)
{
load.Load<Survey>(this); // I'm telling it the type and passing it the object
}
public class FakeLoadForSurvey : ILoad //this class knows about Survey
{
public void Load<T>(T t)
{
t = new Survey(); //fails
{
}
It fails with the following error message
Cannot implicity convert type Survey to T
I hope the above example is clear enough.
How do I tell C# that T is of type Survey (or any other type)?
public class FakeLoadForSurvey : ILoad //this class knows about Survey
If the class implementing the interface knows the type for T, then move T to the interface:
public interface ILoad<T>
{
void Load(T t);
}
public class FakeLoadForSurvey : ILoad<Survey>
{
public void Load(Survey t)
{
t = new Survey();
}
}
Do note however that this won't affect the argument passed to Load().
So if some code calls it like this:
var surveyLoader = new FakeLoadForSurvey();
Survey surveyToLoad = null;
surveyLoader.Load(surveyLoader);
Then surveyToLoad is still null after Load() returns. If you don't want that, pass it with ref.
This code looks bad from a design point of view, since you are mixing generics with statically defined types. If you use generics, you should go all the way:
public interface ILoad
{
void Load<T>(T t) where T : new();
}
public class FakeLoadForSurvey : ILoad
{
public void Load<T>(T t) where T : new()
{
t = new T();
}
}
I am not sure what your intention is with the parameter you define, but it loses scope after the method, so t will never of any use outside of the Load<T> method.
public void Load<T>(T t) where T: Survey, new()
{
t = (T) new Survey(); // should succeed
}
But with a void return and without ref on the parameter this function still won't do anything useful.

How to create an Action with generic argument?

example
i have a class
using System;
namespace ConsoleApp1
{
public interface INode<T>
{
T Value { get; }
}
public class A
{
public void Do1(INode<string> node) { }
public void Do2<T>(INode<T> node) { }
}
public class Programm
{
public static void Main(string[] args)
{
A a = new A();
//now I want to pass this method to an another class as arguments
//it's OK here
var processor1 = new Proccessor(a.Do1);
//no idea how to achieve this without making class Proccessor generic type
var processor2 = new Proccessor(a.Do2);
}
}
public class Proccessor
{
//it's OK here
public Proccessor(Action<INode<string>> do1Action)
{
}
//no idea how to achieve this without making class Proccessor generic type
public Proccessor(Action<T><INode<T>> do2Action)
{
}
}
}
It looks like you're trying to pass an Action<T> to a method such that the method can then choose which T to invoke it for. That's simply not possible.
The easiest way around this is avoiding generics entirely, by making all BaseNodeViewModel<T, U> derive from a common non-generic BaseNodeViewModel type. Your OnNodeExpanded then looks like
private void OnNodeExpanded(BaseNodeViewModel node) { ... }
and it may cast node to the concrete type as needed in its implementation.
A more advanced way around this is creating a custom interface type
internal interface INodeExpanded {
void OnNodeExpanded<TNode>(BaseNodeViewModel<TNode, FolderTreeViewModel> node);
}
You can then manually create a class which implements this interface, and pass that to A instead of a delegate.
The answer to this question depends on what exactly you expect to be able to do with do2Action inside the class.
Do you to say "I want to accept and store an action that will be able to work with any INode"? Assuming that's the case, make INode covariant, and demand an Action<INode<object>>. Like so:
public interface INode<out T>
{
T Value { get; }
}
Then...
public Proccessor(Action<INode<object>> do2Action)
{
}
Now you can do:
var processor2 = new Proccessor((Action<INode<object>>)a.Do2(x));
The cast is required to disambiguate from the string overload, but if you remove the overloads and instead use distinctly named static methods to construct, you will find that this is type safe.
Note that an immense responsibility has now been pushed to a.Do2, it must be able to work with any INode whatsoever, without restriction.
You can't have a generic constructor for a non-generic class. You have at least 3 options (Asad's answer is also very good if your intentions are what he specified in his answer):
don't use generics. see hvd's answer.
make the class generic.
don't use T in the constructor, but in another method. You can have a static method that will create and return an instance of the class for you, there you can use the generic action:
public class Proccessor
{
//it's OK here
public Proccessor(Action<INode<string>> do1Action)
{
}
private Proccessor()
{
// a private constructor for the CreateProcessor static method
}
public static Proccessor CreateProccessor<T>(Action<INode<T>> do2Action)
{
var proccessor = new Proccessor();
// invoke action here
}
}

C# Generics - Infer Type of static factory members

I would like to invoke static members of a class that uses generics without specifying the type and having the compiler infer them.
For example this is the generic class I want to use, with the Static Factory member:
public class GenericClass<T>
{
public T Member;
// Constructor
public GenericClass(T value)
{
Member = value;
}
// static factory method
public static GenericClass<T> StaticFactory(T resultData)
{
return new GenericClass<T>(resultData);
}
}
If I try the following does not compile:
public class Class1
{
public GenericClass<string> returnResult1()
{
return GenericClass.StaticFactory("Won't Compile, but it's clear that T is a string");
}
public GenericClass returnResult2()
{
return GenericClass.StaticFactory("Won't Compile, but it's clear that T is a string");
}
}
Error 1 Using the generic type 'SampleStatic.GenericClass' requires 1 type arguments
Why can't I do like the following with static members?
public void ICanInferTheType<T1>(T1 item);
public void returnResult4()
{
ICanInferTheType("Compiles, recognizes type string");
}
Thanks -
Generic inference only happens for method calls. In your case, you need it for referring to a class. Given class GenericClass<T> { ... }, any reference to GenericClass without a type parameter is a compilation error. The only time generic parameters can be inferred is for generic parameters declared on method signatures.
However, you can achieve your goal anyway. If you declare this additional class, you'll be able to invoke the method the way you want. That's because the generic parameters are moved to the method.
public static class GenericClass {
// static factory method
public static GenericClass<T> StaticFactory<T>(T resultData) {
return new GenericClass<T>(resultData);
}
}
Ok, thanks to #recursive! That answer is almost perfect, it requires one more in the name of the method, like this:
public static class GenericClass
{
// static factory method
public static GenericClass<T> StaticFactory<T>(T resultData)
{
return new GenericClass<T>(resultData);
}
}

return object of type that is passed to class

Let's say I have the class
class SomeClass<T>
{
public static void someMethod(T param1) // this gives an error
// I have to do something like (var param1)
{
// ...
// ...
}
}
How could require param1 to be of type T so that I can call the method as:
SomeOtherClass object1 = new SomeOtherClass();
SomeClass<SomeOtherClass>.someMethod(object1);
class SomeClass<T>
{
public static void someMethod<T>(T myobject)
{
}
}
Just use a generic method instead.
public static void SomeMethod<T>(T param1) ...
I do not know C# very well, but I think you would want to apply the generic type definition to the method rather than the class.
public static void someMethod<T>(T param1)
{
//not sure where <T> definition goes in signature...
}
What error do you get? It is probably related to something other than the method signature. This works fine for me:
class SomeClass<T>
{
public static void someMethod(T param1)
{
Console.WriteLine(param1);
}
}
...
SomeClass<int>.someMethod(4);
Tono your code is right. According to what Brandon says (and dislike some other pretend), you don't have to make your method generic since the whole class it belongs to is already generic... except if you'd wish to have a type parameter for this method different from the class' type parameter :
class SomeClass<T>
{
public static void someMethod<U>(U myobject)
{
}
}
Which, obviously, is absolutely not what you want.
By the way in danyolgiax' answer, the compiler will raise an error because of the conflict name between the class' type parameter named T and the method's type parameter also (badly) named T.
It is sure your error comes from somewhere else. Why not mentioning the exact error message you get ? This will be half way to the solution. :-)

C# Deriving Generic Methods

When i need to pass a Generic Type I can use the Syntax
(Example : Ofcourse it is not a Generic Method)
public void Sample(T someValue)
{
......
}
What is the benefit of declaring Sample<T> ?
I mean
public void Sample<T> (T someValue)
{
......
}
Generic types and generic methods are very different things. It sounds like you have a generic type:
class SomeType<T> {
public void Sample(T someValue) {...}
}
and are discussing having a generic method inside it:
class SomeType<T> {
public void Sample<T>(T someValue) {...}
}
These are very different. In the first, inside Sample, then T means "the T that got passed to SomeType<T>". In the second, inside Sample, this is a separate and independent T - "the T that got passed to Sample<T>". In fact, calling it T (in this case) is a mistake. You could have, for example:
var obj = new SomeType<int>(); // here T for SomeType<T> is int
obj.Sample<decimal>(123.45M); // here T for Sample<T> is decimal
Note that there is no easy way (within Sample<T>) of saying "the T in SomeType<T>" - hence why you should rename the method's generic type parameter.
There are valid scenarios for this type of scenario (generic methods on generic types), for example (and note the new name):
class SomeType<T> {
public void Sample<TActual>(TActual someValue) where TActual : T, new() {...}
}
This allows us to do some very interesting things in terms of inheritance, etc - or you might want a generic method that has little or no relation to T. That is fine too.
For this to work:
public void Sample(T someValue)
{
......
}
The type T has to be declared in the system already. And the method will only accept the type T or its derivatives.
By declaring this:
public void Sample<T> (T someValue)
{
......
}
you say the method will accept any type that comes.
Consider the following:
class SomeClass<T>
{
public void Sample(T value)
{
// code goes here
}
}
or this:
class SomeClass
{
public void Sample<T>(T value)
{
// code goes here
}
}
In the first case, for all calls to Sample in a specific instance T will be the same type. In the second case each call in a specific instance can have its own type of T, since the generic type argument is supplied with the method call.
The second approach can have many uses, but one that I have used myself sometimes is in factory methods:
public static class SomeFactory
{
public static T CreateSomeObject<T>()
{
T result = Activator.CreateInstance<T>();
// perform any extra initialization
return result;
}
}
You can use the 2nd example if your class isn't generic. It means you can make just that method generic. If your class is generic, you should use your 1st example.

Categories

Resources