How do I convert IEnumerable<T> to string, recursively? - c#

I want a function that I can call as an alternative to .ToString(), that will show the contents of collections.
I've tried this:
public static string dump(Object o) {
if (o == null) return "null";
return o.ToString();
}
public static string dump<K, V>(KeyValuePair<K, V> kv) {
return dump(kv.Key) + "=>" + dump(kv.Value);
}
public static string dump<T>(IEnumerable<T> list) {
StringBuilder result = new StringBuilder("{");
foreach(T t in list) {
result.Append(dump(t));
result.Append(", ");
}
result.Append("}");
return result.ToString();
}
but the second overload never gets called. For example:
List<string> list = new List<string>();
list.Add("polo");
Dictionary<int, List<string>> dict;
dict.Add(1, list);
Console.WriteLine(dump(dict));
I'm expecting this output:
{1=>{"polo", }, }
What actually happens is this:
dict is correctly interpreted as an IEnumerable<KeyValuePair<int, List<string>>>, so the 3rd overload is called.
the 3rd overload calls dump on a KeyValuePair>. This should(?) invoke the second overload, but it doesn't -- it calls the first overload instead.
So we get this output:
{[1=>System.Collections.Generic.List`1[System.String]], }
which is built from KeyValuePair's .ToString() method.
Why isn't the second overload called? It seems to me that the runtime should have all the information it needs to identify a KeyValuePair with full generic arguments and call that one.

Generics is a compile time concept, not run time.
In other words the type parametes are resolved at compile time.
In your foreach you call dump(t) and t is of type T.
But there is nothing known about T at this point other than that it is an Object.
That's why the first overload is called.

(updated) As mentioned in other answers, the problem is that the compiler does not know that type V is actually a List<string>, so it just goes to dump(object).
A possible workaround might be to check types at run time. Type.IsGenericType will tell you if the type of a variable has generics or not, and Type.GetGenericArguments will give you the actual type of those generics.
So you can write a single dump method receiving an object and ignoring any generics info. Note that I use the System.Collections.IEnumerable interface rather than System.Collections.Generics.IEnumerable<T>.
public static string dump(Object o)
{
Type type = o.GetType();
// if it's a generic, check if it's a collection or keyvaluepair
if (type.IsGenericType) {
// a collection? iterate items
if (o is System.Collections.IEnumerable) {
StringBuilder result = new StringBuilder("{");
foreach (var i in (o as System.Collections.IEnumerable)) {
result.Append(dump(i));
result.Append(", ");
}
result.Append("}");
return result.ToString();
// a keyvaluepair? show key => value
} else if (type.GetGenericArguments().Length == 2 &&
type.FullName.StartsWith("System.Collections.Generic.KeyValuePair")) {
StringBuilder result = new StringBuilder();
result.Append(dump(type.GetProperty("Key").GetValue(o, null)));
result.Append(" => ");
result.Append(dump(type.GetProperty("Value").GetValue(o, null)));
return result.ToString();
}
}
// arbitrary generic or not generic
return o.ToString();
}
That is: a) a collection is iterated, b) a keyvaluepair shows key => value, c) any other object just calls ToString. With this code
List<string> list = new List<string>();
list.Add("polo");
Dictionary<int, List<string>> dict = new Dictionary<int, List<string>>() ;
dict.Add(1, list);
Console.WriteLine(dump(list));
Console.WriteLine(dump(dict.First()));
Console.WriteLine(dump(dict));
you get the expected output:
{marco, }
1 => {marco, }
{1 => {marco, }, }

To call the second version in your foreach, you need to specify the template parameters K and V, otherwise it will always call the first version:
dump(t); // always calls first version
dump<K,V>(t); // will call the second
How you get the parameter types K and V is another question....

Related

Cannot deconstruct dynamic object while calling dll's method

Say I have some dll with a method like so:
public (string, List<string>) MyMethod(NameValueCollection Settings, MyClass1 params)
{
//do something
return (result, errorList);
}
Now from my main project I will call it like so:
var shipmentNumber = string.Empty;
var errorList = new List<string>;
var DLL = Assembly.LoadFile($#"{AppDomain.CurrentDomain.BaseDirectory}{appSettings[$"{parameters.TestCase}_DLL_Name"]}");
Type classType;
classType = DLL.GetType($"{appSettings[$"{parameters.TestCase}_DLL_Name"].Replace(".dll", "")}.MyService");
dynamic d = Activator.CreateInstance(classType);
(result, errorList)= d.MyMethod(appSettings, params);
However this gives me an error on the last line shown here Cannot deconstruct dynamic objects. Is there a way I could return tuple properly here?
As per the compiler error message, you can't use deconstruction with dynamic values.
In this case you know that your method is going to return a tuple, so either cast the result to that:
(result, errorList) = ((string, List<string>)) d.MyMethod(appSettings, params);
Or assign to a tuple and then deconstruct:
(string, List<string>) tuple = d.MyMethod(appSettings, params);
(result, errorList) = tuple;
Note that the casting looks a bit funky with the double parentheses, but they're necessary: the outer parentheses are for casting syntax; the inner parentheses are for tuple type syntax.
Here's a complete simple example:
using System;
class Test
{
static void Main()
{
dynamic d = new Test();
// Variables we want to deconstruct into
string text;
int number;
// Approach 1: Casting
(text, number) = ((string, int)) d.Method();
// Approach 2: Assign to a tuple variable first
(string, int) tuple = d.Method();
(text, number) = tuple;
}
public (string, int) Method() => ("text", 5);
}

Function that returns different types?

Suppose I have a container class that contains two properties:
string contents
bool isCommaSeperated
And a function:
? compute()
{
if(this.isCommaSeperated)
{
return contents.split(",").toList();
}
else
{
return contents;
}
}
Is there a way for this function to return either a string or a list of strings?
Or what type of design would allow me to achieve something similar?
I would just return both results as a collection:
IList<string> compute()
{
if (this.isCommaSeperated)
{
return contents.Split(","); // No need to turn the array into a list
}
return new[] { contents };
}
You can use dynamic to implement that:
dynamic Compute(bool isCommaSeperated)
{
if(isCommaSeperated)
{
return contents.Split(",").ToList();
}
else
{
return contents;
}
}
Code will still preserve type information, but let you return any type you like and fail at run-time if you try to use methods of another type.
Note that you give up compile type safety by doing it. Depending on your needs it may be ok, but consider is some alternative solution that preserve compile type safety would work better. I.e. returning single element array as shown in istme86's asnwer.
While IEnumerable<string> is the standard solution, you could also try the new StringValues struct which implicitly casts between string values and string arrays:
StringValues a = "A";
StringValues b = new string[] { "A", "B" };
StringValues c = null;
Console.WriteLine(a.Contains("A"));
// true
Console.WriteLine(b.Any(x => x == a));
// true
Console.WriteLine(c.Any());
// false

Casting to a Tuple<object,object>

Consider this chunk of code where I'm testing an unknown variable that could be an int, MyObj, or Tuple, and I'm doing some type checking to see what it is so that I can go on and handle the data differently depending on what it is:
class MyObj { }
// ...
void MyMethod(object data) {
if (data is int) Console.Write("Datatype = int");
else if (data is MyObj) Console.Write("Datatype = MyObj");
else if (data is Tuple<object,object>) {
var myTuple = data as Tuple<object,object>;
if (myTuple.Item1 is int && myTuple.Item2 is int) Console.WriteLine("Datatype = Tuple<int,int>");
else if (myTuple.Item1 is int && myTuple.Item2 is MyObj) Console.WriteLine("Datatype = Tuple<int,MyObj>");
// other type checks
}
}
// ...
MyMethod(1); // Works : Datatype = int
MyMethod(new MyObj()); // Works : Datatype = MyObj
MyMethod(Tuple.Create(1, 2)); // Fails
MyMethod(Tuple.Create(1, new MyObj()); // Fails
// and also...
var items = new List<object>() {
1,
new MyObj(),
Tuple.Create(1, 2),
Tuple.Create(1, new MyObj())
};
foreach (var o in items) MyMethod(o);
My problem is that a Tuple<int,MyObj> doesn't cast to Tuple<object,object>, yet individually, I can cast int to object and MyObj to object.
How do I do the cast?
If you want to check for any pair type, you'll have to use reflection:
Type t = data.GetType();
if(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Tuple<,>))
{
var types = t.GetGenericArguments();
Console.WriteLine("Datatype = Tuple<{0}, {1}>", types[0].Name, types[1].Name)
}
You may be able to use overloading and dynamic instead of manually inspecting the type:
MyMethod(MyObject obj) { ... }
MyMethod(int i) { ... }
MyMethod(Tuple<int, int> t) { ... }
MyMethod(Tuple<int, MyObject> t) { ... }
foreach(dynamic d in items)
{
MyMethod(d);
}
this will choose the best overload at runtime so you have access the tuple types directly.
As you can see here, Tuple<T1, T2> is not covariant.
Rather than trying to make one method that can take any type of parameter, why not overload your function to recieve valid types you actually expect.
perhaps,
void MyMethod<T1,T2>(Tuple<T1, T2> data)
{
// In case ToString() is overridden
Console.WriteLine("Datatype = Tuple<{0}, {1}>",
typeof(T1).Name,
typeof(T2).Name);
}
void MyMethod(MyObj data)
{
Console.WriteLine("Datatype = MyObj");
}
void MyMethod(int data)
{
Console.WriteLine("Datatype = System.Int32");
}
This way no type checking is required, the compiler does it for you at compile time. This is not javascript, strong typing can be a benefit.
In your code you are explicitly checking against int and MyObj, as well as against their position (Item1 or Item2) as seen here:
if (myTuple.Item1 is int && myTuple.Item2 is int)
else if (myTuple.Item1 is int && myTuple.Item2 is MyObj)
You can do the same explicit check using the same framework you already wrote:
if (data is int) Console.WriteLine("Datatype = int");
else if (data is MyObj) Console.WriteLine("Datatype = MyObj");
else if (data is Tuple<int, int>) Console.WriteLine("Datatype = Tuple<int,int>");
else if (data is Tuple<int, MyObj>) Console.WriteLine("Datatype = Tuple<int,MyObj>");
The downsides to the above code are the same as what you were attempting to do with Tuple<object, object>: You have to explicitly check all combinations of what could go into the tuple as well as their positions, i.e., my code will not work as written if you pass in Tuple<double, double> or Tuple<MyObj, int>, but neither would your code if it actually worked as you wanted it to. If you don't want explicit/hardcoded values then it appears you have to use reflection as seen in Lee's answer, otherwise the above code does what you were intending with less work.

Can I pass arbitrary number of named parameters to function in C#?

Is there some kind of equivalent of Python's **kwargs in C#? I would like to be able to pass variable number of named arguments into functon, then get them as something Dictionary-like inside function and cycle over them.
There is nothing in C# available to let you pass in arbitrary named parameters like this.
You can get close by adding a Dictionary<string, object> parameter, which lets you do something similar but requiring a constructor, the "parameter names" to be strings and some extra braces:
static void Method(int normalParam, Dictionary<string, object> kwargs = null)
{
...
}
Method(5, new Dictionary<String, object>{{ "One", 1 }, { "Two", 2 }});
You can get closer by using the ObjectToDictionaryRegistry here, which lets you pass in an anonymous object which doesn't require you to name a dictionary type, pass the parameter names as strings or add quite so many braces:
static void Method(int normalParam, object kwargs = null)
{
Dictionary<string, object> args = ObjectToDictionaryRegistry(kwargs);
...
}
Method(5, new { One = 1, Two = 2 });
However, this involves dynamic code generation so will cost you in terms of performance.
In terms of syntax, I doubt you'll ever be able to get rid of the `new { ... }' wrapper this requires.
If you specifically want a series of KeyValuePairs instead of an array of values, you could do something like this:
public class Foo
{
public Foo(params KeyValuePair<object, object>[] myArgs)
{
var argsDict = myArgs.ToDictionary(k=>k.Key, v=>v.Value);
// do something with argsDict
}
}
myArgs would be an array of KeyValuePair<object, object> that you can iterate or convert to a dictionary as shown above. Just a note though, the conversion to dictionary might fail if you pass multiple KeyValuePair<>s with the same key. You might have to do some checking ahead of time before converting to a dictionary.
You would call it like this:
KeyValuePair<object, object> myKvp1 = new KeyValuePair<object, object>(someKey1, someValue1);
KeyValuePair<object, object> myKvp2 = new KeyValuePair<object, object>(someKey2, someValue2);
KeyValuePair<object, object> myKvp3 = new KeyValuePair<object, object>(someKey3, someValue3);
Foo foo = new Foo(myKvp1, myKvp2, myKvp3);
Yes. The ability to use optional and named parameters was added in, I believe, C# 4.0.
http://msdn.microsoft.com/en-us/library/dd264739(v=vs.100).aspx
void paramsExample(object arg1, object arg2, params object[] argsRest)
{
foreach (object arg in argsRest)
{ /* .... */ }
}
call it like this,
paramsExample(1, 0.0f, "a string", 0.0m, new UserDefinedType());
You can use a param list as your final argument to the function, like this:
void paramsExample(object arg1, object arg2, params object[] argsRest) {
foreach (object arg in argsRest)
{ /* .... */ }
}
the method can be invoked with any number of arguments of any type.
paramsExample(1, 0.0f, "a string", 0.0m, new UserDefinedType());
This is a strongly typed argument so if I wanted to use a list of strings I could do this:
public string Concat(string separator, params string[] strings) {
string result = "";
for (int i = 0; i < strings.Length; i++) {
if (i > 0)
result += separator;
result += strings[i];
}
return result;
}
To invoke:
MessageBox.Show(Concat("+", "Anders", "Eric", "Scott", "Duncan") + " = great team");
Check out more info here:
http://blogs.msdn.com/b/csharpfaq/archive/2004/05/13/how-do-i-write-a-method-that-accepts-a-variable-number-of-parameters.aspx

C# Reflection - How can I tell if object o is of type KeyValuePair and then cast it?

I'm currently trying to write a Dump() method from LinqPad equivalent iin C# for my own amusment. I'm moving from Java to C# and this is an exercise rather than a business requirement. I've got almost everything working except for Dumping a Dictionary.
The problem is that KeyValuePair is a Value type. For most other Value types I simply call the ToString method but this is insufficient as the KeyValuePair may contain Enumerables and other objects with undesirable ToString methods. So I need to work out if it's a KeyValuePair and then cast it. In Java I could use wildcard generics for this but I don't know the equivalent in C#.
Your quest, given an object o, determine if it's a KeyValuePair and call Print on its key and value.
Print(object o) {
...
}
Thanks!
If you don't know the types stored in the KeyValuePair you need to exercise a bit of reflection code.
Let's look at what is needed:
First, let's ensure the value isn't null:
if (value != null)
{
Then, let's ensure the value is generic:
Type valueType = value.GetType();
if (valueType.IsGenericType)
{
Then, extract the generic type definition, which is KeyValuePair<,>:
Type baseType = valueType.GetGenericTypeDefinition();
if (baseType == typeof(KeyValuePair<,>))
{
Then extract the types of the values in it:
Type[] argTypes = baseType.GetGenericArguments();
Final code:
if (value != null)
{
Type valueType = value.GetType();
if (valueType.IsGenericType)
{
Type baseType = valueType.GetGenericTypeDefinition();
if (baseType == typeof(KeyValuePair<,>))
{
Type[] argTypes = baseType.GetGenericArguments();
// now process the values
}
}
}
If you've discovered that the object does indeed contain a KeyValuePair<TKey,TValue> you can extract the actual key and value like this:
object kvpKey = valueType.GetProperty("Key").GetValue(value, null);
object kvpValue = valueType.GetProperty("Value").GetValue(value, null);
Presuming you are using the generic KeyValuePair then you need probably want to test for a particular instantiation, such as one created using a string for both key and value:
public void Print(object o)
{
if (o == null)
return;
if (o is KeyValuePair<string, string>)
{
KeyValuePair<string, string> pair = (KeyValuePair<string, string>)o;
Console.WriteLine("{0} = {1}", pair.Key, pair.Value);
}
}
If you want to test for any type of KeyValuePair then you'll need to use reflection. Do you?
you have the is keyword
http://msdn.microsoft.com/es-es/library/scekt9xw(VS.80).aspx

Categories