How to compile a lambda expression into a dynamically created class - c#

I'm trying to create an extension of a class using reflection emit.
And it is kinda working.
var extendwith = new List<Type>();
extendwith.Add(typeof(object));
var t = Utils.DynamicInherit<BaseTest>("Extended",typeof(UtilsTest).GetMethod(nameof(Testing)), extendwith);
var instance = Activator.CreateInstance(t, new TestClass(), new { A=1 });
public class BaseTest
{
public readonly TestClass testClass;
public object IsNotNull;
public BaseTest(TestClass testClass)
{
this.testClass = testClass;
}
}
public static void Testing(BaseTest baseTest, List<object> objects)
{
foreach(var t in objects)
{
baseTest.IsNotNull = t;
}
}
What is happening here is that the the type (t) returned from the DynamicInherit method will now inherit from the BaseTest class and have a constructor containing 2 parameters (TestClass, Object)
The static method Testing will be called inside the constructor of the "constructed type".
It is called using this IL code.
constructorBuilder.Emit(OpCodes.Ldarg_0); //The this a referance to the created base class
constructorBuilder.Emit(OpCodes.Ldloc_0); //Reference to a List<Objects> that is passed in thorugh the constructor
constructorBuilder.Emit(OpCodes.Call, constructor); //The function to be called.. in this case the "public static void Testing(BaseTest baseTest, List<object> objects)"
This is working "as expected", but I would like to change it to using a lamda expression instead of the static function..
var t = Utils.DynamicInherit<BaseTest>("Extended",(baseO, objects) => {foreach(var t in objects)
{
baseTest.IsNotNull = t;
}} ,
extendwith);
I know I have to change the line
constructorBuilder.Emit(OpCodes.Call, constructor);
But I'm not able to figure out how....
Any suggestions???

Given
Action<BaseText,List<object>> f = (BaseTest baseO, List<object> objects) => {
foreach(var t in objects) {
baseTest.IsNotNull = t;
}
};
Then f.Method should be what you need I think:
var t = Utils.DynamicInherit<BaseTest>("Extended",f.Method, extendwith);
Alternatively, you can combine it all:
var t = Utils.DynamicInherit<BaseTest>("Extended",((Action<BaseTest,List<object>>)((baseO, objects) => {foreach(var t in objects)
{
baseTest.IsNotNull = t;
}})).Method,
extendwith);
Using a utility class I have makes this a little easier to read:
public static class To {
public static Func<TResult> Func<TResult>(Func<TResult> func) => func;
public static Func<T, TResult> Func<T, TResult>(Func<T, TResult> func) => func;
public static Func<T1, T2, TResult> Func<T1, T2, TResult>(Func<T1, T2, TResult> func) => func;
public static Func<T1, T2, T3, TResult> Func<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> func) => func;
public static Func<T1, T2, T3, T4, TResult> Func<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> func) => func;
public static Func<T1, T2, T3, T4, T5, TResult> Func<T1, T2, T3, T4, T5, TResult>(Func<T1, T2, T3, T4, T5, TResult> func) => func;
public static Func<T1, T2, T3, T4, T5, T6, TResult> Func<T1, T2, T3, T4, T5, T6, TResult>(Func<T1, T2, T3, T4, T5, T6, TResult> func) => func;
public static Func<T1, T2, T3, T4, T5, T6, T7, TResult> Func<T1, T2, T3, T4, T5, T6, T7, TResult>(Func<T1, T2, T3, T4, T5, T6, T7, TResult> func) => func;
public static Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> func) => func;
public static Action<T> Action<T>(Action<T> act) => act;
public static Action<T1, T2> Action<T1, T2>(Action<T1, T2> act) => act;
public static Action<T1, T2, T3> Action<T1, T2, T3>(Action<T1, T2, T3> act) => act;
public static Action<T1, T2, T3, T4> Action<T1, T2, T3, T4>(Action<T1, T2, T3, T4> act) => act;
public static Action<T1, T2, T3, T4, T5> Action<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> act) => act;
public static Action<T1, T2, T3, T4, T5, T6> Action<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> act) => act;
public static Action<T1, T2, T3, T4, T5, T6, T7> Action<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> act) => act;
public static Action<T1, T2, T3, T4, T5, T6, T7, T8> Action<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> act) => act;
}
Then you just have:
var t = Utils.DynamicInherit<BaseTest>("Extended", To.Action((baseO, objects) => {
foreach(var t in objects) {
baseTest.IsNotNull = t;
}}).Method,
extendwith);

Related

How to set item name in c# tuple? [duplicate]

Is there a way to use a Tuple class, but supply the names of the items in it?
For example:
public Tuple<int, int, int int> GetOrderRelatedIds()
That returns the ids for OrderGroupId, OrderTypeId, OrderSubTypeId and OrderRequirementId.
It would be nice to let the users of my method know which is which. (When you call the method, the results are result.Item1, result.Item2, result.Item3, result.Item4. It is not clear which one is which.)
(I know I could just create a class to hold all these Ids, but it these Ids already have their own classes they live in and making a class for this one method's return value seems silly.)
In C# 7.0 (Visual Studio 2017) there is a new construction to do that:
(string first, string middle, string last) LookupName(long id)
Up to C# 7.0, there was no way to do this short of defining your own type.
Here is an overly complicated version of what you are asking:
class MyTuple : Tuple<int, int>
{
public MyTuple(int one, int two)
:base(one, two)
{
}
public int OrderGroupId { get{ return this.Item1; } }
public int OrderTypeId { get{ return this.Item2; } }
}
Why not just make a class?
Reproducing my answer from this post as it is a better fit here.
Starting C# v7.0, it is now possible to name the tuple properties which earlier used to default to names like Item1, Item2 and so on.
Naming the properties of Tuple Literals:
var myDetails = (MyName: "Foo", MyAge: 22, MyFavoriteFood: "Bar");
Console.WriteLine($"Name - {myDetails.MyName}, Age - {myDetails.MyAge}, Passion - {myDetails.MyFavoriteFood}");
The output on console:
Name - Foo, Age - 22, Passion - Bar
Returning Tuple (having named properties) from a method:
static void Main(string[] args)
{
var empInfo = GetEmpInfo();
Console.WriteLine($"Employee Details: {empInfo.firstName}, {empInfo.lastName}, {empInfo.computerName}, {empInfo.Salary}");
}
static (string firstName, string lastName, string computerName, int Salary) GetEmpInfo()
{
//This is hardcoded just for the demonstration. Ideally this data might be coming from some DB or web service call
return ("Foo", "Bar", "Foo-PC", 1000);
}
The output on console:
Employee Details: Foo, Bar, Foo-PC, 1000
Creating a list of Tuples having named properties
var tupleList = new List<(int Index, string Name)>
{
(1, "cow"),
(5, "chickens"),
(1, "airplane")
};
foreach (var tuple in tupleList)
Console.WriteLine($"{tuple.Index} - {tuple.Name}");
Output on console:
1 - cow
5 - chickens
1 - airplane
Note: Code snippets in this post are using string interpolation feature of C# v6 as detailed here.
TL:DR -> System.ValueTuples can have custom names for fields, System.Tuples cannot.
Just to clarify, there are 2 different types of tuples in C# 7.0 and later.
System.Tuple
and
System.ValueTuple
When you declare a tuple via the Tuple<...> class:
public Tuple<int, string, int> GetUserInfo();
You're declaring a Tuple object data type.
When you declare a tuple via parenthesis:
public (int id, string name, int age) GetUserInfo();
You're declaring a ValueTuple value data type.
Each one functions and behaves differently. In your question, your method returns a System.Tuple object.
Unfortunately, Tuple objects created via the System.Tuple class have no built in functionality to give each property a custom name. They always default to ItemN depending on how many properties they contain.
System.ValueTuple values on the other hand can contain custom named fields.
For more info, you can refer to Tuple types (C# reference) and/or the links to each class above. But essentially some of the key differences of the 2 different types of tuples that the documentation highlights are:
C# tuples, which are backed by System.ValueTuple types, are different
from tuples that are represented by System.Tuple types. The main
differences are as follows:
System.ValueTuple types are value types. System.Tuple types are reference types.
System.ValueTuple types are mutable. System.Tuple types are immutable.
Data members of System.ValueTuple types are fields. Data members of System.Tuple types are properties.
So if your method needs to return a System.Tuple object or you desire the behavior of that type of object more, then as of writing this, you can't achieve what you want. However, if your method can return a System.ValueTuple value, then you can give it custom named fields in the returned value.
With .net 4 you could perhaps look at the ExpandoObject, however, don't use it for this simple case as what would have been compile-time errors become run-time errors.
class Program
{
static void Main(string[] args)
{
dynamic employee, manager;
employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
manager = new ExpandoObject();
manager.Name = "Allison Brown";
manager.Age = 42;
manager.TeamSize = 10;
WritePerson(manager);
WritePerson(employee);
}
private static void WritePerson(dynamic person)
{
Console.WriteLine("{0} is {1} years old.",
person.Name, person.Age);
// The following statement causes an exception
// if you pass the employee object.
// Console.WriteLine("Manages {0} people", person.TeamSize);
}
}
// This code example produces the following output:
// John Smith is 33 years old.
// Allison Brown is 42 years old.
Something else worth mentioning is an anonymous type for within a method, but you need to create a class if you want to return it.
var MyStuff = new
{
PropertyName1 = 10,
PropertyName2 = "string data",
PropertyName3 = new ComplexType()
};
MichaelMocko Answered is great,
but I want to add a few things which I had to figure out
(string first, string middle, string last) LookupName(long id)
above Line will give you compile-time error if you are using .net framework < 4.7
So if you have a project that is using .net framework < 4.7 and still you want to use ValueTuple than workAround would be installing this NuGet package
Update:
Example of returning Named tuple from a method and using it
public static (string extension, string fileName) GetFile()
{
return ("png", "test");
}
Using it
var (extension, fileName) = GetFile();
Console.WriteLine(extension);
Console.WriteLine(fileName);
As of today, it's this simple. Instead of using the Tuple keyword
public Tuple<int, int, int int> GetOrderRelatedIds()
Use this.
public (int alpha, int beta, int candor) GetOrderRelatedIds()
Get the values like this.
var a = GetOrderRelatedIds();
var c = a.alpha;
Just to add to #MichaelMocko answer. Tuples have couple of gotchas at the moment:
You can't use them in EF expression trees
Example:
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
return ctx.Persons
.Where(person => person.Id == id)
// Selecting as Tuple
.Select(person => (person.Name, person.Surname))
.First();
}
This will fail to compile with "An expression tree may not contain a tuple literal" error. Unfortunately, the expression trees API wasn't expanded with support for tuples when these were added to the language.
Track (and upvote) this issue for the updates: https://github.com/dotnet/roslyn/issues/12897
To get around the problem, you can cast it to anonymous type first and then convert the value to tuple:
// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
return ctx.Persons
.Where(person => person.Id == id)
.Select(person => new { person.Name, person.Surname })
.ToList()
.Select(person => (person.Name, person.Surname))
.First();
}
Another option is to use ValueTuple.Create:
// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
return ctx.Persons
.Where(person => person.Id == id)
.Select(person => ValueTuple.Create(person.Name, person.Surname))
.First();
}
References:
https://www.damirscorner.com/blog/posts/20181207-NoSupportForTuplesInExpressionTrees.html
Convert anonymous type to new C# 7 tuple type
You can't deconstruct them in lambdas
There's a proposal to add the support: https://github.com/dotnet/csharplang/issues/258
Example:
public static IQueryable<(string name, string surname)> GetPersonName(this PersonContext ctx, int id)
{
return ctx.Persons
.Where(person => person.Id == id)
.Select(person => ValueTuple.Create(person.Name, person.Surname));
}
// This won't work
ctx.GetPersonName(id).Select((name, surname) => { return name + surname; })
// But this will
ctx.GetPersonName(id).Select(t => { return t.name + t.surname; })
References:
C# 7 tuples and lambdas
They won't serialize nicely
using System;
using Newtonsoft.Json;
public class Program
{
public static void Main() {
var me = (age: 21, favoriteFood: "Custard");
string json = JsonConvert.SerializeObject(me);
// Will output {"Item1":21,"Item2":"Custard"}
Console.WriteLine(json);
}
}
Tuple field names are only available at compile time and are completely wiped out at runtime.
References:
Make Value Tuple property names resolvable at runtime (CSharpLang)
C#7 Tuple Support (Newtonsoft.Json)
No, you can't name the tuple members.
The in-between would be to use ExpandoObject instead of Tuple.
If the types of your items are all different, here is a class I made to get them more intuitively.
The usage of this class:
var t = TypedTuple.Create("hello", 1, new MyClass());
var s = t.Get<string>();
var i = t.Get<int>();
var c = t.Get<MyClass>();
Source code:
public static class TypedTuple
{
public static TypedTuple<T1> Create<T1>(T1 t1)
{
return new TypedTuple<T1>(t1);
}
public static TypedTuple<T1, T2> Create<T1, T2>(T1 t1, T2 t2)
{
return new TypedTuple<T1, T2>(t1, t2);
}
public static TypedTuple<T1, T2, T3> Create<T1, T2, T3>(T1 t1, T2 t2, T3 t3)
{
return new TypedTuple<T1, T2, T3>(t1, t2, t3);
}
public static TypedTuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4)
{
return new TypedTuple<T1, T2, T3, T4>(t1, t2, t3, t4);
}
public static TypedTuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
return new TypedTuple<T1, T2, T3, T4, T5>(t1, t2, t3, t4, t5);
}
public static TypedTuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{
return new TypedTuple<T1, T2, T3, T4, T5, T6>(t1, t2, t3, t4, t5, t6);
}
public static TypedTuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{
return new TypedTuple<T1, T2, T3, T4, T5, T6, T7>(t1, t2, t3, t4, t5, t6, t7);
}
public static TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{
return new TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8>(t1, t2, t3, t4, t5, t6, t7, t8);
}
}
public class TypedTuple<T>
{
protected Dictionary<Type, object> items = new Dictionary<Type, object>();
public TypedTuple(T item1)
{
Item1 = item1;
}
public TSource Get<TSource>()
{
object value;
if (this.items.TryGetValue(typeof(TSource), out value))
{
return (TSource)value;
}
else
return default(TSource);
}
private T item1;
public T Item1 { get { return this.item1; } set { this.item1 = value; this.items[typeof(T)] = value; } }
}
public class TypedTuple<T1, T2> : TypedTuple<T1>
{
public TypedTuple(T1 item1, T2 item2)
: base(item1)
{
Item2 = item2;
}
private T2 item2;
public T2 Item2 { get { return this.item2; } set { this.item2 = value; this.items[typeof(T2)] = value; } }
}
public class TypedTuple<T1, T2, T3> : TypedTuple<T1, T2>
{
public TypedTuple(T1 item1, T2 item2, T3 item3)
: base(item1, item2)
{
Item3 = item3;
}
private T3 item3;
public T3 Item3 { get { return this.item3; } set { this.item3 = value; this.items[typeof(T3)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4> : TypedTuple<T1, T2, T3>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4)
: base(item1, item2, item3)
{
Item4 = item4;
}
private T4 item4;
public T4 Item4 { get { return this.item4; } set { this.item4 = value; this.items[typeof(T4)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4, T5> : TypedTuple<T1, T2, T3, T4>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
: base(item1, item2, item3, item4)
{
Item5 = item5;
}
private T5 item5;
public T5 Item5 { get { return this.item5; } set { this.item5 = value; this.items[typeof(T5)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4, T5, T6> : TypedTuple<T1, T2, T3, T4, T5>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
: base(item1, item2, item3, item4, item5)
{
Item6 = item6;
}
private T6 item6;
public T6 Item6 { get { return this.item6; } set { this.item6 = value; this.items[typeof(T6)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4, T5, T6, T7> : TypedTuple<T1, T2, T3, T4, T5, T6>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
: base(item1, item2, item3, item4, item5, item6)
{
Item7 = item7;
}
private T7 item7;
public T7 Item7 { get { return this.item7; } set { this.item7 = value; this.items[typeof(T7)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> : TypedTuple<T1, T2, T3, T4, T5, T6, T7>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8)
: base(item1, item2, item3, item4, item5, item6, item7)
{
Item8 = item8;
}
private T8 item8;
public T8 Item8 { get { return this.item8; } set { this.item8 = value; this.items[typeof(T8)] = value; } }
}
This is very annoying and I expect future versions of C# will address this need. I find the easiest work around to be either use a different data structure type or rename the "items" for your sanity and for the sanity of others reading your code.
Tuple<ApiResource, JSendResponseStatus> result = await SendApiRequest();
ApiResource apiResource = result.Item1;
JSendResponseStatus jSendStatus = result.Item2;
(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.
(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.
From Docs: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples
Why not using a multiple returns instead of using tuple
var handler = GenerateFromMethod1(hits);
Process(handler.string1, handler.string1);
private static (string string1, string string2) GenerateFromMethod1()
{
}
I think I would create a class but another alternative is output parameters.
public void GetOrderRelatedIds(out int OrderGroupId, out int OrderTypeId, out int OrderSubTypeId, out int OrderRequirementId)
Since your Tuple only contains integers you could represent it with a Dictionary<string,int>
var orderIds = new Dictionary<string, int> {
{"OrderGroupId", 1},
{"OrderTypeId", 2},
{"OrderSubTypeId", 3},
{"OrderRequirementId", 4}.
};
but I don't recommend that either.
Why is everyone making life so hard. Tuples are for rather temporary data processing. Working with Tuples all the time will make the code very hard to understand at some point. Creating classes for everything could eventually bloat your project.
It's about balance, however...
Your problem seems to be something you would want a class for. And just for the sake of completeness, this class below also contains constructors.
This is the proper pattern for
A custom data type
with no further functionality. Getters and setters can also be expanded with code, getting/setting private members with the name pattern of "_orderGroupId", while also executing functional code.
Including constructors. You can also choose to include just one constructor if all properties are mandatory.
If you want to use all constructors, bubbling like this is the proper pattern to avoid duplicate code.
public class OrderRelatedIds
{
public int OrderGroupId { get; set; }
public int OrderTypeId { get; set; }
public int OrderSubTypeId { get; set; }
public int OrderRequirementId { get; set; }
public OrderRelatedIds()
{
}
public OrderRelatedIds(int orderGroupId)
: this()
{
OrderGroupId = orderGroupId;
}
public OrderRelatedIds(int orderGroupId, int orderTypeId)
: this(orderGroupId)
{
OrderTypeId = orderTypeId;
}
public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId)
: this(orderGroupId, orderTypeId)
{
OrderSubTypeId = orderSubTypeId;
}
public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId, int orderRequirementId)
: this(orderGroupId, orderTypeId, orderSubTypeId)
{
OrderRequirementId = orderRequirementId;
}
}
Or, if you want it really simple: You can also use type initializers:
OrderRelatedIds orders = new OrderRelatedIds
{
OrderGroupId = 1,
OrderTypeId = 2,
OrderSubTypeId = 3,
OrderRequirementId = 4
};
public class OrderRelatedIds
{
public int OrderGroupId;
public int OrderTypeId;
public int OrderSubTypeId;
public int OrderRequirementId;
}
I would write the Item names in the summay..
so by hovering over the function helloworld() the text will say hello = Item1 and world = Item2
helloworld("Hi1,Hi2");
/// <summary>
/// Return hello = Item1 and world Item2
/// </summary>
/// <param name="input">string to split</param>
/// <returns></returns>
private static Tuple<bool, bool> helloworld(string input)
{
bool hello = false;
bool world = false;
foreach (var hw in input.Split(','))
{
switch (hw)
{
case "Hi1":
hello= true;
break;
case "Hi2":
world= true;
break;
}
}
return new Tuple<bool, bool>(hello, world);
}
You Can write a class that contains the Tuple.
You need to override the Equals and GetHashCode functions
and the == and != operators.
class Program
{
public class MyTuple
{
private Tuple<int, int> t;
public MyTuple(int a, int b)
{
t = new Tuple<int, int>(a, b);
}
public int A
{
get
{
return t.Item1;
}
}
public int B
{
get
{
return t.Item2;
}
}
public override bool Equals(object obj)
{
return t.Equals(((MyTuple)obj).t);
}
public override int GetHashCode()
{
return t.GetHashCode();
}
public static bool operator ==(MyTuple m1, MyTuple m2)
{
return m1.Equals(m2);
}
public static bool operator !=(MyTuple m1, MyTuple m2)
{
return !m1.Equals(m2);
}
}
static void Main(string[] args)
{
var v1 = new MyTuple(1, 2);
var v2 = new MyTuple(1, 2);
Console.WriteLine(v1 == v2);
Dictionary<MyTuple, int> d = new Dictionary<MyTuple, int>();
d.Add(v1, 1);
Console.WriteLine(d.ContainsKey(v2));
}
}
will return:
True
True
C# 7 tuple example
var tuple = TupleExample(key, value);
private (string key1, long value1) ValidateAPIKeyOwnerId(string key, string value)
{
return (key, value);
}
if (!string.IsNullOrEmpty(tuple.key1) && tuple.value1 > 0)
{
//your code
}

How can you name a tuple used as a function parameter in C#? [duplicate]

Is there a way to use a Tuple class, but supply the names of the items in it?
For example:
public Tuple<int, int, int int> GetOrderRelatedIds()
That returns the ids for OrderGroupId, OrderTypeId, OrderSubTypeId and OrderRequirementId.
It would be nice to let the users of my method know which is which. (When you call the method, the results are result.Item1, result.Item2, result.Item3, result.Item4. It is not clear which one is which.)
(I know I could just create a class to hold all these Ids, but it these Ids already have their own classes they live in and making a class for this one method's return value seems silly.)
In C# 7.0 (Visual Studio 2017) there is a new construction to do that:
(string first, string middle, string last) LookupName(long id)
Up to C# 7.0, there was no way to do this short of defining your own type.
Here is an overly complicated version of what you are asking:
class MyTuple : Tuple<int, int>
{
public MyTuple(int one, int two)
:base(one, two)
{
}
public int OrderGroupId { get{ return this.Item1; } }
public int OrderTypeId { get{ return this.Item2; } }
}
Why not just make a class?
Reproducing my answer from this post as it is a better fit here.
Starting C# v7.0, it is now possible to name the tuple properties which earlier used to default to names like Item1, Item2 and so on.
Naming the properties of Tuple Literals:
var myDetails = (MyName: "Foo", MyAge: 22, MyFavoriteFood: "Bar");
Console.WriteLine($"Name - {myDetails.MyName}, Age - {myDetails.MyAge}, Passion - {myDetails.MyFavoriteFood}");
The output on console:
Name - Foo, Age - 22, Passion - Bar
Returning Tuple (having named properties) from a method:
static void Main(string[] args)
{
var empInfo = GetEmpInfo();
Console.WriteLine($"Employee Details: {empInfo.firstName}, {empInfo.lastName}, {empInfo.computerName}, {empInfo.Salary}");
}
static (string firstName, string lastName, string computerName, int Salary) GetEmpInfo()
{
//This is hardcoded just for the demonstration. Ideally this data might be coming from some DB or web service call
return ("Foo", "Bar", "Foo-PC", 1000);
}
The output on console:
Employee Details: Foo, Bar, Foo-PC, 1000
Creating a list of Tuples having named properties
var tupleList = new List<(int Index, string Name)>
{
(1, "cow"),
(5, "chickens"),
(1, "airplane")
};
foreach (var tuple in tupleList)
Console.WriteLine($"{tuple.Index} - {tuple.Name}");
Output on console:
1 - cow
5 - chickens
1 - airplane
Note: Code snippets in this post are using string interpolation feature of C# v6 as detailed here.
TL:DR -> System.ValueTuples can have custom names for fields, System.Tuples cannot.
Just to clarify, there are 2 different types of tuples in C# 7.0 and later.
System.Tuple
and
System.ValueTuple
When you declare a tuple via the Tuple<...> class:
public Tuple<int, string, int> GetUserInfo();
You're declaring a Tuple object data type.
When you declare a tuple via parenthesis:
public (int id, string name, int age) GetUserInfo();
You're declaring a ValueTuple value data type.
Each one functions and behaves differently. In your question, your method returns a System.Tuple object.
Unfortunately, Tuple objects created via the System.Tuple class have no built in functionality to give each property a custom name. They always default to ItemN depending on how many properties they contain.
System.ValueTuple values on the other hand can contain custom named fields.
For more info, you can refer to Tuple types (C# reference) and/or the links to each class above. But essentially some of the key differences of the 2 different types of tuples that the documentation highlights are:
C# tuples, which are backed by System.ValueTuple types, are different
from tuples that are represented by System.Tuple types. The main
differences are as follows:
System.ValueTuple types are value types. System.Tuple types are reference types.
System.ValueTuple types are mutable. System.Tuple types are immutable.
Data members of System.ValueTuple types are fields. Data members of System.Tuple types are properties.
So if your method needs to return a System.Tuple object or you desire the behavior of that type of object more, then as of writing this, you can't achieve what you want. However, if your method can return a System.ValueTuple value, then you can give it custom named fields in the returned value.
With .net 4 you could perhaps look at the ExpandoObject, however, don't use it for this simple case as what would have been compile-time errors become run-time errors.
class Program
{
static void Main(string[] args)
{
dynamic employee, manager;
employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
manager = new ExpandoObject();
manager.Name = "Allison Brown";
manager.Age = 42;
manager.TeamSize = 10;
WritePerson(manager);
WritePerson(employee);
}
private static void WritePerson(dynamic person)
{
Console.WriteLine("{0} is {1} years old.",
person.Name, person.Age);
// The following statement causes an exception
// if you pass the employee object.
// Console.WriteLine("Manages {0} people", person.TeamSize);
}
}
// This code example produces the following output:
// John Smith is 33 years old.
// Allison Brown is 42 years old.
Something else worth mentioning is an anonymous type for within a method, but you need to create a class if you want to return it.
var MyStuff = new
{
PropertyName1 = 10,
PropertyName2 = "string data",
PropertyName3 = new ComplexType()
};
MichaelMocko Answered is great,
but I want to add a few things which I had to figure out
(string first, string middle, string last) LookupName(long id)
above Line will give you compile-time error if you are using .net framework < 4.7
So if you have a project that is using .net framework < 4.7 and still you want to use ValueTuple than workAround would be installing this NuGet package
Update:
Example of returning Named tuple from a method and using it
public static (string extension, string fileName) GetFile()
{
return ("png", "test");
}
Using it
var (extension, fileName) = GetFile();
Console.WriteLine(extension);
Console.WriteLine(fileName);
As of today, it's this simple. Instead of using the Tuple keyword
public Tuple<int, int, int int> GetOrderRelatedIds()
Use this.
public (int alpha, int beta, int candor) GetOrderRelatedIds()
Get the values like this.
var a = GetOrderRelatedIds();
var c = a.alpha;
Just to add to #MichaelMocko answer. Tuples have couple of gotchas at the moment:
You can't use them in EF expression trees
Example:
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
return ctx.Persons
.Where(person => person.Id == id)
// Selecting as Tuple
.Select(person => (person.Name, person.Surname))
.First();
}
This will fail to compile with "An expression tree may not contain a tuple literal" error. Unfortunately, the expression trees API wasn't expanded with support for tuples when these were added to the language.
Track (and upvote) this issue for the updates: https://github.com/dotnet/roslyn/issues/12897
To get around the problem, you can cast it to anonymous type first and then convert the value to tuple:
// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
return ctx.Persons
.Where(person => person.Id == id)
.Select(person => new { person.Name, person.Surname })
.ToList()
.Select(person => (person.Name, person.Surname))
.First();
}
Another option is to use ValueTuple.Create:
// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
return ctx.Persons
.Where(person => person.Id == id)
.Select(person => ValueTuple.Create(person.Name, person.Surname))
.First();
}
References:
https://www.damirscorner.com/blog/posts/20181207-NoSupportForTuplesInExpressionTrees.html
Convert anonymous type to new C# 7 tuple type
You can't deconstruct them in lambdas
There's a proposal to add the support: https://github.com/dotnet/csharplang/issues/258
Example:
public static IQueryable<(string name, string surname)> GetPersonName(this PersonContext ctx, int id)
{
return ctx.Persons
.Where(person => person.Id == id)
.Select(person => ValueTuple.Create(person.Name, person.Surname));
}
// This won't work
ctx.GetPersonName(id).Select((name, surname) => { return name + surname; })
// But this will
ctx.GetPersonName(id).Select(t => { return t.name + t.surname; })
References:
C# 7 tuples and lambdas
They won't serialize nicely
using System;
using Newtonsoft.Json;
public class Program
{
public static void Main() {
var me = (age: 21, favoriteFood: "Custard");
string json = JsonConvert.SerializeObject(me);
// Will output {"Item1":21,"Item2":"Custard"}
Console.WriteLine(json);
}
}
Tuple field names are only available at compile time and are completely wiped out at runtime.
References:
Make Value Tuple property names resolvable at runtime (CSharpLang)
C#7 Tuple Support (Newtonsoft.Json)
No, you can't name the tuple members.
The in-between would be to use ExpandoObject instead of Tuple.
If the types of your items are all different, here is a class I made to get them more intuitively.
The usage of this class:
var t = TypedTuple.Create("hello", 1, new MyClass());
var s = t.Get<string>();
var i = t.Get<int>();
var c = t.Get<MyClass>();
Source code:
public static class TypedTuple
{
public static TypedTuple<T1> Create<T1>(T1 t1)
{
return new TypedTuple<T1>(t1);
}
public static TypedTuple<T1, T2> Create<T1, T2>(T1 t1, T2 t2)
{
return new TypedTuple<T1, T2>(t1, t2);
}
public static TypedTuple<T1, T2, T3> Create<T1, T2, T3>(T1 t1, T2 t2, T3 t3)
{
return new TypedTuple<T1, T2, T3>(t1, t2, t3);
}
public static TypedTuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4)
{
return new TypedTuple<T1, T2, T3, T4>(t1, t2, t3, t4);
}
public static TypedTuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
return new TypedTuple<T1, T2, T3, T4, T5>(t1, t2, t3, t4, t5);
}
public static TypedTuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{
return new TypedTuple<T1, T2, T3, T4, T5, T6>(t1, t2, t3, t4, t5, t6);
}
public static TypedTuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{
return new TypedTuple<T1, T2, T3, T4, T5, T6, T7>(t1, t2, t3, t4, t5, t6, t7);
}
public static TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{
return new TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8>(t1, t2, t3, t4, t5, t6, t7, t8);
}
}
public class TypedTuple<T>
{
protected Dictionary<Type, object> items = new Dictionary<Type, object>();
public TypedTuple(T item1)
{
Item1 = item1;
}
public TSource Get<TSource>()
{
object value;
if (this.items.TryGetValue(typeof(TSource), out value))
{
return (TSource)value;
}
else
return default(TSource);
}
private T item1;
public T Item1 { get { return this.item1; } set { this.item1 = value; this.items[typeof(T)] = value; } }
}
public class TypedTuple<T1, T2> : TypedTuple<T1>
{
public TypedTuple(T1 item1, T2 item2)
: base(item1)
{
Item2 = item2;
}
private T2 item2;
public T2 Item2 { get { return this.item2; } set { this.item2 = value; this.items[typeof(T2)] = value; } }
}
public class TypedTuple<T1, T2, T3> : TypedTuple<T1, T2>
{
public TypedTuple(T1 item1, T2 item2, T3 item3)
: base(item1, item2)
{
Item3 = item3;
}
private T3 item3;
public T3 Item3 { get { return this.item3; } set { this.item3 = value; this.items[typeof(T3)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4> : TypedTuple<T1, T2, T3>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4)
: base(item1, item2, item3)
{
Item4 = item4;
}
private T4 item4;
public T4 Item4 { get { return this.item4; } set { this.item4 = value; this.items[typeof(T4)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4, T5> : TypedTuple<T1, T2, T3, T4>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
: base(item1, item2, item3, item4)
{
Item5 = item5;
}
private T5 item5;
public T5 Item5 { get { return this.item5; } set { this.item5 = value; this.items[typeof(T5)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4, T5, T6> : TypedTuple<T1, T2, T3, T4, T5>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
: base(item1, item2, item3, item4, item5)
{
Item6 = item6;
}
private T6 item6;
public T6 Item6 { get { return this.item6; } set { this.item6 = value; this.items[typeof(T6)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4, T5, T6, T7> : TypedTuple<T1, T2, T3, T4, T5, T6>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
: base(item1, item2, item3, item4, item5, item6)
{
Item7 = item7;
}
private T7 item7;
public T7 Item7 { get { return this.item7; } set { this.item7 = value; this.items[typeof(T7)] = value; } }
}
public class TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> : TypedTuple<T1, T2, T3, T4, T5, T6, T7>
{
public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8)
: base(item1, item2, item3, item4, item5, item6, item7)
{
Item8 = item8;
}
private T8 item8;
public T8 Item8 { get { return this.item8; } set { this.item8 = value; this.items[typeof(T8)] = value; } }
}
This is very annoying and I expect future versions of C# will address this need. I find the easiest work around to be either use a different data structure type or rename the "items" for your sanity and for the sanity of others reading your code.
Tuple<ApiResource, JSendResponseStatus> result = await SendApiRequest();
ApiResource apiResource = result.Item1;
JSendResponseStatus jSendStatus = result.Item2;
(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.
(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.
From Docs: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples
Why not using a multiple returns instead of using tuple
var handler = GenerateFromMethod1(hits);
Process(handler.string1, handler.string1);
private static (string string1, string string2) GenerateFromMethod1()
{
}
I think I would create a class but another alternative is output parameters.
public void GetOrderRelatedIds(out int OrderGroupId, out int OrderTypeId, out int OrderSubTypeId, out int OrderRequirementId)
Since your Tuple only contains integers you could represent it with a Dictionary<string,int>
var orderIds = new Dictionary<string, int> {
{"OrderGroupId", 1},
{"OrderTypeId", 2},
{"OrderSubTypeId", 3},
{"OrderRequirementId", 4}.
};
but I don't recommend that either.
Why is everyone making life so hard. Tuples are for rather temporary data processing. Working with Tuples all the time will make the code very hard to understand at some point. Creating classes for everything could eventually bloat your project.
It's about balance, however...
Your problem seems to be something you would want a class for. And just for the sake of completeness, this class below also contains constructors.
This is the proper pattern for
A custom data type
with no further functionality. Getters and setters can also be expanded with code, getting/setting private members with the name pattern of "_orderGroupId", while also executing functional code.
Including constructors. You can also choose to include just one constructor if all properties are mandatory.
If you want to use all constructors, bubbling like this is the proper pattern to avoid duplicate code.
public class OrderRelatedIds
{
public int OrderGroupId { get; set; }
public int OrderTypeId { get; set; }
public int OrderSubTypeId { get; set; }
public int OrderRequirementId { get; set; }
public OrderRelatedIds()
{
}
public OrderRelatedIds(int orderGroupId)
: this()
{
OrderGroupId = orderGroupId;
}
public OrderRelatedIds(int orderGroupId, int orderTypeId)
: this(orderGroupId)
{
OrderTypeId = orderTypeId;
}
public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId)
: this(orderGroupId, orderTypeId)
{
OrderSubTypeId = orderSubTypeId;
}
public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId, int orderRequirementId)
: this(orderGroupId, orderTypeId, orderSubTypeId)
{
OrderRequirementId = orderRequirementId;
}
}
Or, if you want it really simple: You can also use type initializers:
OrderRelatedIds orders = new OrderRelatedIds
{
OrderGroupId = 1,
OrderTypeId = 2,
OrderSubTypeId = 3,
OrderRequirementId = 4
};
public class OrderRelatedIds
{
public int OrderGroupId;
public int OrderTypeId;
public int OrderSubTypeId;
public int OrderRequirementId;
}
I would write the Item names in the summay..
so by hovering over the function helloworld() the text will say hello = Item1 and world = Item2
helloworld("Hi1,Hi2");
/// <summary>
/// Return hello = Item1 and world Item2
/// </summary>
/// <param name="input">string to split</param>
/// <returns></returns>
private static Tuple<bool, bool> helloworld(string input)
{
bool hello = false;
bool world = false;
foreach (var hw in input.Split(','))
{
switch (hw)
{
case "Hi1":
hello= true;
break;
case "Hi2":
world= true;
break;
}
}
return new Tuple<bool, bool>(hello, world);
}
You Can write a class that contains the Tuple.
You need to override the Equals and GetHashCode functions
and the == and != operators.
class Program
{
public class MyTuple
{
private Tuple<int, int> t;
public MyTuple(int a, int b)
{
t = new Tuple<int, int>(a, b);
}
public int A
{
get
{
return t.Item1;
}
}
public int B
{
get
{
return t.Item2;
}
}
public override bool Equals(object obj)
{
return t.Equals(((MyTuple)obj).t);
}
public override int GetHashCode()
{
return t.GetHashCode();
}
public static bool operator ==(MyTuple m1, MyTuple m2)
{
return m1.Equals(m2);
}
public static bool operator !=(MyTuple m1, MyTuple m2)
{
return !m1.Equals(m2);
}
}
static void Main(string[] args)
{
var v1 = new MyTuple(1, 2);
var v2 = new MyTuple(1, 2);
Console.WriteLine(v1 == v2);
Dictionary<MyTuple, int> d = new Dictionary<MyTuple, int>();
d.Add(v1, 1);
Console.WriteLine(d.ContainsKey(v2));
}
}
will return:
True
True
C# 7 tuple example
var tuple = TupleExample(key, value);
private (string key1, long value1) ValidateAPIKeyOwnerId(string key, string value)
{
return (key, value);
}
if (!string.IsNullOrEmpty(tuple.key1) && tuple.value1 > 0)
{
//your code
}

Flat map discriminated unions

I'm using a lib off Github called OneOf.
Basically you have one type that can be one of many types, but in a statically safe way.
Below is an example of what I'm trying to achieve.
I have type A<T1, T2, T3, T4>, and type B<T1, T2, T3>, I'd like to "flapmap" type B into type A. Since type A can accept any single T from B, it should be possible.
The compiler forces me to extract each T from B before assigning it to A, as you can see by the silly x => x lambdas below.
I don't want to end up with some like A<B<T1, T2, T3>, T4> either.
So can anyone think of basically a SelectMany for these OneOf types?
using OneOf;
using System;
using System.IO;
namespace ScratchPad
{
class Program
{
struct BadRequest { }
struct Error { }
struct NotFound { }
static void Main(string[] arg)
{
string result = GetFile(#"c:\data\foo.txt").Match(
text => text,
badRequest => "filepath cannot be null",
notFound => "filepath does not exist",
error => "an error occurred"
);
Console.WriteLine(result);
}
static OneOf<string, BadRequest, NotFound, Error> GetFile(string filepath)
{
OneOf<string, BadRequest, NotFound, Error> response = new BadRequest();
if (filepath != null)
{
// How can I make the type from ReadText() automatically convert to the type of the response local variable, without having to write these silly lambda?
response = ReadText(filepath).Match<OneOf<string, BadRequest, NotFound, Error>>(x => x, x => x, x => x);
}
return response;
}
static OneOf<string, NotFound, Error> ReadText(string filepath)
{
OneOf<string, NotFound, Error> response = new NotFound();
try
{
if (File.Exists(filepath))
{
response = File.ReadAllText(filepath);
}
}
catch
{
response = new Error();
}
return response;
}
}
}
Extension methods could be a way.
An example, just out of my head, converting 3 to 4 types:
public static class OneOfExtensions
{
public static OneOf<T1, T2, T3, T4> ConvertOneOf<T1, T2, T3, T4>(this OneOf<T1, T2, T3> oneOf)
{
return oneOf.Match<OneOf<T1, T2, T3, T4>>(x => x, x => x, x=> x);
}
public static OneOf<T1, T2, T3, T4> ConvertOneOf<T1, T2, T3, T4>(this OneOf<T1, T2, T4> oneOf)
{
return oneOf.Match<OneOf<T1, T2, T3, T4>>(x => x, x => x, x=> x);
}
public static OneOf<T1, T2, T3, T4> ConvertOneOf<T1, T2, T3, T4>(this OneOf<T1, T3, T4> oneOf)
{
return oneOf.Match<OneOf<T1, T2, T3, T4>>(x => x, x => x, x=> x);
}
public static OneOf<T1, T2, T3, T4> ConvertOneOf<T1, T2, T3, T4>(this OneOf<T2, T3, T4> oneOf)
{
return oneOf.Match<OneOf<T1, T2, T3, T4>>(x => x, x => x, x=> x);
}
}
It's not particularly pretty, but it should keep your code relatively clean.

String- Function dictionary c# where functions have different arguments

Basically I'm trying to make a string to function dictionary in c#, I've seen it done like this:
Dictionary<string, Func<string, string>>
However the issue is that the functions I want to put into my dictionary all have different amounts of arguments of different types. Therefore how do I make a dictionary that will do this?
Adam
You can define your own delegate taking a params string[] argument, like this:
delegate TOut ParamsFunc<TIn, TOut>(params TIn[] args);
And declares your dictionary like so:
Dictionary<string, ParamsFunc<string, string>> functions;
So, you can use it like this:
public static string Concat(string[] args)
{
return string.Concat(args);
}
var functions = new Dictionary<string, ParamsFunc<string, string>>();
functions.Add("concat", Concat);
var concat = functions["concat"];
Console.WriteLine(concat()); //Output: ""
Console.WriteLine(concat("A")); //Output: "A"
Console.WriteLine(concat("A", "B")); //Output: "AB"
Console.WriteLine(concat(new string[] { "A", "B", "C" })); //Output: "ABC"
Be aware that you still need to declare your methods with a string[] argument, even if you only need one string parameter.
On the other hand, it can be called using params style (like concat() or concat("A", "B")).
You can use Dictionary<string, Delegate>. To call a function stored in a Delegate, use the DynamicInvoke() method.
I'd go with the ExpandoObject which was specifically meant to be able to have the CLR support dynamic languages (see IronPython, et al).
static void Main()
{
dynamic expando = new ExpandoObject();
expando.Do = new Func<string>(MyFunc);
expando.Do2 = new Func<string, string>(MyFunc2);
Console.WriteLine(expando.Do());
Console.WriteLine(expando.Do2("args"));
}
static string MyFunc()
{
return "Do some awesome stuff";
}
static string MyFunc2(string arg)
{
return "Do some awesome stuff with " + arg;
}
First of all, if you have to use a method dictionary, implement your methods to have the same signature ! (Or really similar whith less diffences possible).
If not, you can still transform your method signature with lambda expression.
Lambda expressions
static string Method1(string a)
{
return a;
}
static void Method2(string a, string b)
{
Console.WriteLine(a + b);
}
static string Method3(string a, string b, int x)
{
return String.Format("a:{0} b:{1} x:{2}", a, b, x);
}
static int Method4(int x)
{
return x;
}
static void Main(string[] args)
{
var methods = new Dictionary<string, Func<int, string, string, string, string>>()
{
{ "method1", (x, a, b, c) => Method1(a) },
{ "method2", (x, a, b, c) => { Method2(a, b); return ""; } },
{ "method3", (x, a, b, c) => Method3(a, b, x) },
{ "method4", (x, a, b, c) => Method4(x).ToString() },
};
foreach (var key in methods.Keys)
Console.WriteLine(key + ": " + methods[key](1, "a", "b", "c"));
Console.ReadKey();
}
As you can see, you will have to maintain the method signature for you dictionary to have all the possible arguments. IT'S UGLY !
But this will work, you won't have to care about which method is behind which string. They will all use the same parameters. But you will have to be very cautious when calling them since, you can easily make a mistake when you call them in the lambda expressions due to the big number of parameters that you will pass.
Structure/Dictionary argument passing
struct method_parameters
{
public string a;
public string b;
public int x;
}
static string Method1(method_parameters parameters)
{
return parameters.a;
}
static void Method2(method_parameters parameters)
{
Console.WriteLine(parameters.a + parameters.b);
}
static string Method3(method_parameters parameters)
{
return String.Format("a:{0} b:{1} x:{2}",
parameters.a, parameters.b, parameters.x);
}
static int Method4(method_parameters parameters)
{
return parameters.x;
}
static void Main(string[] args)
{
method_parameters parameters = new method_parameters()
{
a = "a",
b = "b",
x = 1
};
var methods = new Dictionary<string, Func<method_parameters, string>>()
{
{ "method1", Method1 },
{ "method2", (param) => { Method2(param); return ""; } },
{ "method3", Method3 },
{ "method4", (param) => Method4(param).ToString() },
};
foreach (var key in methods.Keys)
Console.WriteLine(key + ": " + methods[key](parameters));
Console.ReadKey();
}
This method is easier to maintain, since you will not have to update each method and entries of your dictionary if you have to add/change an argument.
You will just have to modify your stucture/class/dictionary depending on what you chose as parameter storage.
But to call a method you will have to update your structure first. And clear it if necessary !
Considering your use case of text based rpg with commands with a set of arguements, perhaps you should make all of your commands methods that all have a signature of
MethodName(string[] args)
With the Dictionary of
new Dictionary><string, Action<string[]>>
That would make it easier to work with your dictionary and actually call the methods.
(edited)
One easy but nasty solution could be something like this,
private void methodDictionary()
{
var infos = new Dictionary<string, MethodInfo>();
infos.Add("a", this.GetType().GetMethod("a"));
infos.Add("b", this.GetType().GetMethod("b"));
MethodInfo a = infos["a"];
a.Invoke(this, new[] { "a1", "b1" });
MethodInfo b = infos["b"];
b.Invoke(this, new object[] { 10, "b1", 2.056 });
}
public void a(string a, string b)
{
Console.WriteLine(a);
Console.WriteLine(b);
}
public void b(int a, string b, double c)
{
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
}
One possible approach to this problem is to create a wrapper class to your own dictionary that simply stores the function delegates as an object. You can then use generic methods to add a retrieve the function that match your signatures.
The following code example shows a very basic implementation:
class FunctionDictionary
{
/// <summary>
/// Internal dictionary that will store the function delegates as Object.
/// </summary>
private Dictionary<string, Object> m_Map = new Dictionary<string, object>();
/// <summary>
/// Add method to dictionary for specified key. Encapsulated method has no parameters.
/// </summary>
public void Add<TResult>(string key, Func<TResult> function)
{
m_Map.Add(key, function);
}
/// <summary>
/// Get method for specified key. Encapsulated method has no parameters.
/// </summary>
public Func<TResult> Function<TResult>(string key)
{
return (Func<TResult>)m_Map[key];
}
/// <summary>
/// Add method to dictionary for specified key. Encapsulated method has one parameters.
/// </summary>
public void Add<T1, TResult>(string key, Func<T1, TResult> function)
{
m_Map.Add(key, function);
}
/// <summary>
/// Get method for specified key. Encapsulated method has one parameters.
/// </summary>
public Func<T, TResult> Function<T, TResult>(string key)
{
return (Func<T, TResult>)m_Map[key];
}
public void Add<T1, T2, TResult>(string key, Func<T1, T2, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, TResult>(string key, Func<T1, T2, T3, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, TResult>(string key, Func<T1, T2, T3, T4, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, TResult>(string key, Func<T1, T2, T3, T4, T5, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> function)
{
m_Map.Add(key, function);
}
public void Add<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>(string key, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> function)
{
m_Map.Add(key, function);
}
public Object this[string key]
{
get
{
return m_Map[key];
}
}
public Func<T1, T2, TResult> Function<T1, T2, TResult>(string key)
{
return (Func<T1, T2, TResult>)m_Map[key];
}
public Func<T1, T2, T3, TResult> Function<T1, T2, T3, TResult>(string key)
{
return (Func<T1, T2, T3, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, TResult> Function<T1, T2, T3, T4, TResult>(string key)
{
return (Func<T1, T2, T3, T4, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, TResult> Function<T1, T2, T3, T4, T5, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, TResult> Function<T1, T2, T3, T4, T5, T6, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, TResult> Function<T1, T2, T3, T4, T5, T6, T7, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> Function<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> Function<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> Function<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> Function<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> Function<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> Function<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> Function<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> Function<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult>)m_Map[key];
}
public Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> Function<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>(string key)
{
return (Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>)m_Map[key];
}
}
The following code example demonstrates the usage:
class FunctionMapUsage
{
private FunctionDictionary functions = new FunctionDictionary();
public string FunctionA()
{
return "A";
}
public string FunctionB(int value)
{
return value.ToString();
}
public int FunctionC(string str1, string str2)
{
return str1.Length + str2.Length;
}
public void CreateFunctionMap()
{
functions.Add<string>("A", FunctionA); // Add Function A to map
functions.Add<int, string>("B", FunctionB); // Add Function B to map
functions.Add<string, string, int>("C", FunctionC); // Add Function C to map
}
public void CallFunctions()
{
var functionA = functions.Function<string>("A"); // Get Function A
var functionB = functions.Function<int, string>("B"); // Get Function B
var functionC = functions.Function<string, string, int>("C"); // Get Function C
string resultA = functionA();
string resultB = functionB(123);
int resultC = functionC("parameter 1", "parameter 2");
}
}
The CreateFunctionMap method simply adds new functions to the dictionary.
The CallFunctions methods show how to extract and execute functions from the dictionary.
There are a few ways to accomplish this, but there is a reason there are no good answers. If you were able to do this you would still need more logic around calling the functions, unless your functions were to only have differing numbers of strings. Then each function could do it's own handling of the arguments.
With that said you could do this:
public delegate string DelegateAction(params string[] args);
public Dictionary<string, DelegateAction> Actions = new Dictionary<string, DelegateAction>();
public void InitializeDictionary()
{
Actions.Add("walk",Move);
}
public string Move(params string[] args)
{
if (args.Length > 0)
{
if (!string.IsNullOrWhiteSpace(args[0]))
{
switch (args[0].ToLower())
{
case "forward":
return "You move forward at a leisurely pace";
case "right":
case "left":
case "backward":
throw new NotImplementedException("Still need to set these up");
default:
return "You need to specify a valid direction (forward,backward,right,left).";
}
}
}
return "You need to specify a direction.";
}
public string ProcessAction(string action, params string[] args)
{
return Actions[action.ToLower()].Invoke(args);
}
Let it be said, if you are going to do this, keys are case sensitive so you will need to use either lowercase/ToLower() or UPPERCASE/ToUpper() for them. You can handle your other parameters many ways, some which you can use other case insensitive matching, but with the switch-case in this example the cases must match also.
Good Luck!
One way to address this issue is to introduce an argument container/command class that would contain the super set of function arguments, including the action type itself (the key from the dictionary in the above question):
public class ActionCommand
{
public ActionType ActionType { get; set; } // enum instead of string
public int Distance { get; set; }
public DirectionType DirectionType { get; set; }
// More properties as needed
}
Then, for each ActionType implement a strategy class:
public interface IActionHandlerStrategy
{
bool AppliesTo(ActionCommand actionCommand);
string Apply(ActionCommand actionCommand);
}
public class WalkActionHandlerStrategy : IActionHandlerStrategy
{
public bool AppliesTo(ActionCommand actionCommand)
{
return ActionCommand.ActionType == ActionType.Walk;
}
public ActionResult Apply(ActionCommand actionCommand)
{
// Do something here and return the result of Walk action
return new ActionResult(...); // Container for results
}
}
public class JumpActionHandlerStrategy : IActionHandlerStrategy
{
public bool AppliesTo(ActionCommand actionCommand)
{
return ActionCommand.ActionType == ActionType.Jump;
}
public ActionResult Apply(ActionCommand actionCommand)
{
// Do something here and return the result of Jump action
return new ActionResult(...); // Container for results
}
}
Add more strategies as needed and implement a class that would apply them:
public class ActionHandler
{
// Register strategies with the handler; typically done with DI
protected IList<IActionHandlerStrategy> ActionHandlerStrategies = new List<IActionHandlerStrategy>
{
new JumpActionHandlerStrategy(),
new WalkActionHandlerStrategy(),
// More strategies here...
};
public ActionResult Handle(ActionCommand actionCommand)
{
var actionHandlerStrategy = ActionHandlerStrategies.FirstOrDefault(ahs => ahs.AppliesTo(actionCommand);
if (actionHandlerStrategy == null)
{
throw new Exception($"No strategy found for action type {actionCommand.ActionType}");
}
return actionHandlerStrategy.Apply(actionCommand);
}
}
The end result should allow for relatively simple addition of new actions without much impact on existing logic.

is it possible to convert Func<T1,T2,TResult> to Func<T2,T1,TResult>

First, foo is a Func<T1,T2,TResult> object.
Is is possible do something like
Func<T2,T1,TResult> bar = ConvertFunction(foo);
thus convert Func<T1,T2,TResult> to Func<T2,T1,TResult>.
Yes, that's possible:
Func<T2, T1, TResult> bar = (t2, t1) => foo(t1, t2);
That basically creates another delegate with switched parameters that internally simply calls the original delegate.
This is the only way to perform this kind of "conversion" if you only have a Func<T1, T2, TResult> and not a Expression<Func<T1, T2, TResult>>.
Here's the function:
class MyFuncConverter<T1, T2, TResult>
{
static Func<T1, T2, TResult> _foo;
public static Func<T2, T1, TResult> ConvertFunction(Func<T1, T2, TResult> foo)
{
_foo = foo;
return new Func<T2, T1, TResult>(MyFunc);
}
private static TResult MyFunc(T2 arg2, T1 arg1)
{
return _foo(arg1, arg2);
}
}
Sample usage:
static void Main(string[] args)
{
var arg1 = 10;
var arg2 = "abc";
// create a Func with parameters in reversed order
Func<string, int, string> testStringInt =
MyFuncConverter<int, string, string>.ConvertFunction(TestIntString);
var result1 = TestIntString(arg1, arg2);
var result2 = testStringInt(arg2, arg1);
// testing results
Console.WriteLine(result1 == result2);
}
/// <summary>
/// Sample method
/// </summary>
private static string TestIntString(int arg1, string arg2)
{
byte[] toEncodeAsBytes = System.Text.ASCIIEncoding.ASCII
.GetBytes(arg2.ToString() + arg1);
string returnValue = System.Convert.ToBase64String(toEncodeAsBytes);
return returnValue;
}

Categories