Suppose i have this delegate's declaration:
private delegate UInt32 Feedback(UInt32 value);
And here i try to use it with lambda expression
feedback = (Feedback)Delegate.Combine(feedback,
value => { Console.WriteLine("Lambda item = " + value); return 0; });
But i get error: Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type
But it works this way
feedback = (Feedback)Delegate.Combine(feedback,
new Func<UInt32, UInt32>(value => { Console.WriteLine("Lambda item = " + value); return 0; }));
I have thought that C# compiler must do it itself.
feedback = (Feedback)Delegate.Combine(feedback,
(Feedback)(value => { Console.WriteLine("Lambda item = " + value); return 0; }));
You must explicitly say the type of a lambda function, otherwise the compiler doesn't know what type it has. For example see http://blogs.msdn.com/b/jaredpar/archive/2007/12/14/c-lambda-type-inference.aspx
One of the limitations of C# type inference is that you cannot use it to infer the type of a lambda expression. For example, the following code will not compile
var f = () => 4;
Lambda expressions do not have type in C# unless you cast them.It's because one lambda can be convertible to more than one delegate type so compiler can't decide which one to choose.And also there is no way to determine what are the type of lamda arguments since you are not specifying them.
Lambda doesn't have a type. It is implicitly convertible to compatible delegate type.
Delegate.Combine takes Delegate as parameter, Since Delegate is an abstract class without any signature, lambda expressions cannot be converted into Delegate. You can only convert a lambda to a concrete type which has a compatible signature.
If you expect the compiler to convert your lambda to Delegate type which is an abstract class and thus can't be instantiated --Which concrete type you would like the compiler to choose? You'll have to specify it.
You can specify the type explicitly like this:
feedback = (Feedback)Delegate.Combine(feedback, new Feedback(value => { Console.WriteLine("Lambda item = " + value); return 0; }));
On the other hand if you have the concrete delegate type in LHS, compiler will be happy to compile.
For example: Following is a valid c# code. Because you said the compiler that you need to convert it to Feedback delegate type.
Feedback feedback2 = value => { Console.WriteLine("Lambda item = " + value); return 0; };
Related
The non-generic version of CastRangeAndAdd works fine. If I skip the (object)
var castedList = list.Select(e => (NewType)e).ToList();
then there is a compile error. If I use the (object) then there is an exception "type TypeHintExcel can not be converted to TypeHint" though the cast operator is there. Any idea? (TypeHint and TypeHintExcel are not related so as I understand the "as keyword" can not be used.)
public static explicit operator TypeHint(TypeHintExcel p)
{
var result = new TypeHint()
{
Id = p.Id,
Hint = p.Hint,
ExternalType = p.ExternalType,
AlienType = p.AlienType,
Weight = p.Weight
};
return result;
}
public static class DbSetExtensions
{
public static void CastRangeAndAdd<OldType, NewType>
(this System.Data.Entity.DbSet<NewType> dbCollection,
List<OldType> list)
where NewType : class
{
var castedList = list.Select(e => (NewType)(object)e).ToList();
dbCollection.AddRange(castedList);
}
}
You can't write a generic method to do this. The explicit conversion operation needs to be bound at compile time, but when that method is compiled it has no idea what the source and destination types are, so it can't possibly be bound to your custom conversion operator.
c# is not as flexible as c++
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/differences-between-cpp-templates-and-csharp-generics
C++ allows code that might not be valid for all type parameters in the template, which is then checked for the specific type used as the type parameter. C# requires code in a class to be written in such a way that it will work with any type that satisfies the constraints. For example, in C++ it is possible to write a function that uses the arithmetic operators + and - on objects of the type parameter, which will produce an error at the time of instantiation of the template with a type that does not support these operators. C# disallows this; the only language constructs allowed are those that can be deduced from the constraints.
I have the method below which converts a (non-static) MethodInfo into a compiled Expression (Func) which I can then call.
This works great: I can call it with a method expecting both reference objects and value types.
BUT unlike the original method where I could call a method that had a parameter expecting a double and pass it an int this compiled expression doesn't support that and throws an InvalidCastException.
How do I modify this to support the same type of implicit casting that happens during a normal method call?
Bonus question: should the instanceExp use the DeclaringType or the ReflectedType from the MethodInfo?
public Func<object, object[], object> Create(MethodInfo methodInfo)
{
var methodParams = methodInfo.GetParameters();
var arrayParameter = Expression.Parameter(typeof(object[]), "array");
var arguments =
methodParams.Select((p, i) => Expression.Convert(
Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType))
.Cast<Expression>()
.ToList();
var instanceParameter = Expression.Parameter(typeof(object), "controller");
var instanceExp = Expression.Convert(instanceParameter, methodInfo.DeclaringType);
var callExpression = Expression.Call(instanceExp, methodInfo, arguments);
var bodyExpression = Expression.Convert(callExpression, typeof(object));
return Expression.Lambda<Func<object, object[], object>>(
bodyExpression, instanceParameter, arrayParameter)
.Compile();
}
--- EDIT
The working solution is:
var changeTypeMethod = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(TypeCode) });
var arguments =
methodParams.Select((p, i) =>
!typeof(IConvertible).IsAssignableFrom(p.ParameterType)
// If NOT IConvertible, don't try to convert it
? (Expression)Expression.Convert(
Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType)
:
// Otherwise add an explicit conversion to the correct type to handle int <--> double etc.
(Expression)Expression.Convert(
Expression.Call(changeTypeMethod,
Expression.ArrayAccess(arrayParameter, Expression.Constant(i)),
Expression.Constant(Type.GetTypeCode(p.ParameterType))),
p.ParameterType)
)
.ToList();
The problem is the same as in this piece of C# code:
object a = 123;
double b = (double)a; // InvalidCastException
The reason is that a is an object, so in order to make it a double the cast must unwrap it, and then transform an int to double. The language allows the cast to do only one thing - it will either unwrap or transform, but not both. You need to tell the compiler how to do this cast explicitly by telling it that there is an int wrapped inside the object:
double b = (double)((int)a); // Works
If you could do the same thing in your LINQ expression, your compiled expression will work as well. However, you may not know the actual type of the parameter at the time you generate your expression, so you may want to go for a different strategy - wire in a call to Convert.ChangeType method, which can unwrap and cast at the same time.
Am I missing something or is it not possible to return a value from a lambda function such as..
Object test = () => { return new Object(); };
or
string test = () => { return "hello"; };
I get a build error "Cannot convert lambda expression to type 'string' because it is not a delegate type".
It's like this syntax assigns the lambda rather than the result of the lambda, which I did not expect.
I can achieve the desired functionality by assigning the function to a Func and calling it by name, but is that the only way?
Please no "why would you need to do this?" regarding my example.
Thanks in advance!
It’s possible but you are trying to assign a lambda to a string. – You need to invoke the lambda:
Func<string> f = () => { return "hello"; };
string test = f();
The error message actually says it all:
Cannot convert lambda expression to type 'string'
… that’s exactly the issue here.
If you want to invoke the lambda inline – but really: why? – you can do that too, you just need to first make it into a delegate explicitly:
string test = (new Func<string>(() => { return "hello"; }))();
The code below is valid:
IEnumerable<SomeThing> things = ...;
// map type SomeThing to a new anonymous type, resulting in a strongly typed
// sequence based on an anon type
var newList = things.Select(item =>
{
return new
{
ID = item.ID,
DateUpdatedOrCreated = ((DateTime)(item.DateUpdated ??
item.DateCreated)).ToShortDateString(),
Total = item.Part1 + item.Part2
};
});
newList now appears in Visual Studio as IEnumerable<'a> and is strongly typed with the anonymous type created in the function. That is so cool.
What I can't seem to do is figure out a way to assign just the lambda expression (and not the enumeration) to an implicitly typed variable. Even though the compiler has no problem with the anonymous type in the context above, if I try (say)
var func = (SomeThing item)=> {
return new { ... };
};
I get the error "Cannot assign lambda expression to implicitly-typed local variable". This seems a strange compiler limitation; unless I am missing something, the types are just as non-ambiguous in the 2nd example as they are in the first first: both type parameters are well defined.
Is there any way to do this? Since it's an anonymous type, of course, I don't have any way to use a type to assign it explicitly, so it seems I'd be stuck with making a class for the output type if not.
Update
Shortly after going about my merry way with Jon Skeet's answer, I found a similar dilemma instantiating classes. In case it's not obvious, the same trick can be used to create strongly typed classes using inferred anonymous types.
class Processor<T,U>
{
public Processor(Func<T,U> func) {
}
}
// func is a delegate with anon return type created using method in answer below
var instance = new Processor(func); // does not compile! Requires type arguments!
cannot be created directly, but can be created in much the same way as the trick below:
public static Processor<T,U> Create<T,U>(Func<T,U> func) {
return new Processor<T,U>(func);
}
var instance = Processor.Create(func); // all good
You can do it via type inference:
var func = BuildFunc((SomeThing item) => {
return new { ... };
});
...
static Func<TSource, TResult> BuildFunc<TSource, TResult>(
Func<TSource, TResult> function) {
return function;
}
Note that BuildFunc doesn't really do anything - it just provides the method call needed to get the compiler to do type inference for the generic type arguments for Func<,> - it adds the information that you're interested in Func<,>, basically - that's information which can't be specified as part of a variable declaration, without also specifying the type arguments.
Can anyone advise why this line of code would not compile? It generates CS1660 instead:
s.run_button.Invoke((b) => { b.Enabled = false; },
new object[] { s.run_button });
Visual studio says: Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type
Lambda expressions by themselves have no type and are not convertible to System.Delegate. The Invoke method has the type System.Delegate and hence it won't compile because the lambda expression has no type. You need to provide an explicit type conversion here
s.run_button.Invoke(
(Action<Button>)((b) => { b.Enabled = false; }),
new object[] { s.run_button });
The Invoke method takes a parameter of type Delegate. It was written before lambdas entered our world. The easiest solution for you is to wrap your lambda with an Action. I'm not sure precisely what type "b" is (and neither does the C# compiler, hence the error), so you'll have to pass it in explicitly. Something like:
s.run_button.Invoke(new Action<Button>(b => b.Enabled = false), new object[] { s.run_button });