I have the following method:
public static IQueryable<T> Where<T, P>(this IQueryable<T> source, Func<T, P> predicate, String filter) {
String[] values = _service.GetValues(filter);
}
The variable values is something like:
String[] values = { "1", "2", "C" };
I need to parse all values to the same type of P. So if P is of type int then it would fail because I would not be able to parse C to int. But if P would be of type String then it would pass since all 3 values can be parsed to String.
The types of P would be the basic ones: int, datetime, float, double, string, boolean ...
How to do this?
You could create a new method to do the conversion for you.
public IEnumerable<P> Parse<P>(string[] values) where P : IConvertable // Add constraint for some compile time notification errors
{
foreach (var value in values)
{
yield return (P)Convert.ChangeType(value, typeof(P));
}
}
Another option if you didn't want a new method would be to use this line.
var convertedValues = values.Select(value => (P)Convert.ChangeType(value, typeof(P)));
On the one line option if you add a .ToList() to the end it will do all the conversions at the line of assignment so it will cause the exception to be thrown immediately.
var convertedValues = values.Select(value => (P)Convert.ChangeType(value, typeof(P))).ToList();
Related
Here are types of data that I have
Title = string
Id = int
Why does that
var docs = _context
.Documents
.Select(x => new { x.Title, x.Id }) // it's IQueryable thats why I cannot go straight to the value tuple
.ToList()
.Select(x => (x.Title, (object)x.Id))
.ToList();
require (object) cast before int Id
in order to satisfy this overload?
public void Test(ICollection<(string Text, object Value)> data)
{
}
Without cast:
Argument 1: cannot convert from 'System.Collections.Generic.List<(string Title, int Id)>' to 'System.Collections.Generic.ICollection<(string Text, object Value)>'
Meanwhile this works fine
public void Test((object a, object b) data)
{
}
int myint = 5;
Test(("a", myint));
What's going on?
This is the same reason why a List<long> can't be implicitly converted to a List<int>, but longs can be implicitly converted to int.
If you can accept that List<long> and List<int> are unrelated types, then you should be able to accept that ICollection<(object, int)> and ICollection<(object, object)> are unrelated types.
(object, int) and (object, object) are unrelated value types, just like long and int. The only reason why the compiler is able to convert from int to long them is because the language specification explicitly allowed this "widening value type conversion". Similarly, tuple types can be implicitly converted if each of the element types can be implicitly converted.
If you still don't understand this, consider this code that I could potentially write in the Test method:
data.Add((new object(), new object()));
If a List<(object, int)> were passed in, that Add call would not work, would it?
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);
}
Perhaps the question title is incorrect. I have the following variables
IEnumerable x = // some IEnumerable
System.Type y = // some type
How can iterate over x in order to generate an array with items of type y?
When I look into the internet I found:
public T[] PerformQuery<T>(IEnumerable q)
{
T[] array = q.Cast<T>().ToArray();
return array;
}
Note I cannot call that method PerformQuery becuase y is of type System.Type in other words calling it as PerformQuery<typeof(y)>(x); or PerformQuery<y>(x); will give me a compiler error.
edit
Here is the reason why I have that problem. I have web service where I post to it two things. The type of table I will like to query (example typeof(Customer)), and the actual string query example "Select * from customers"
protected void Page_Load(object sender, EventArgs e)
{
// code to deserialize posted data
Type table = // implement that here
String query = // the query that was posted
// note DB is of type DbContext
IEnumerable q = Db.Database.SqlQuery(table, query );
// here I will like to cast q to an array of items of type table!
You can use Expression Trees:
public static class MyExtensions
{
public static Array ToArray(this IEnumerable source, Type type)
{
var param = Expression.Parameter(typeof(IEnumerable), "source");
var cast = Expression.Call(typeof(Enumerable), "Cast", new[] { type }, param);
var toArray = Expression.Call(typeof(Enumerable), "ToArray", new[] { type }, cast);
var lambda = Expression.Lambda<Func<IEnumerable, Array>>(toArray, param).Compile();
return lambda(source);
}
}
It generates x => x.Cast<Type>().ToArray() for you, with Type known at runtime.
Usage:
IEnumerable input = Enumerable.Repeat("test", 10);
Type type = typeof(string);
Array result = input.ToArray(type);
var ObjectsOfType_y = x.OfType<object>().Where(x => x.GetType() == y);
Notice that this will return an IEnumerable<object>, though. There's no way around that because the type that y (Type) represents is unknown at compile time.
According to my understanding, IENumerable contains only one type. If I understand what you're trying to do, IENumerable already only contains only objects of type y. If y needs to change, you can write an extension method:
public static T[] ToArray<T>(this IEnumerable<T> source)
{
int length = System.Linq.Enumerable.Count(source);
T[] newArray = new T[length];
int i = 0;
foreach(T item in source)
{
newArray[i] = item;
}
return newArray;
}
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....
I have a simple linq-to-sql statement that matches values in a string[] to the values in a table and returns the matched table values.
_table is of type System.Data.Linq.Table<TEntity>
public IEnumerable<ZipCode> match(IEnumerable<string> values) {
HashSet<string> valueSet = new HashSet<string>(values);
return _table.Where(x => valueSet.Contains(x.zipcode);
}
How can I refactor this so that I can pass the field to be used into the Contains()?
My failed attempt:
public IEnumerable<ZipCode> match<T>(IEnumerable<T> values,
Expression<Func<ZipCode, T>> field) {
HashSet<T> valueSet = new HashSet<T>(values);
return _table.Where(x => valueSet.Contains(field(x)));
}
This produces the error:
Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL.
My thought was that I could use the expression tree to give the field for the Contains() but confused on how or if this is even the correct way to use expressions. There is probably a better solution altogether.
I was able to use the following (in .NET 4.5, but I believe it will work in earlier versions)
static IEnumerable<TTable> Match<TTable, TField>(IQueryable<TTable> table, IEnumerable<TField> values, Expression<Func<TTable, TField>> fieldSelector)
{
var valuesConstant = Expression.Constant(values.Distinct());
var callContains = Expression.Call(typeof(Enumerable), "Contains", new[] { typeof(TField) }, new Expression[] { valuesConstant, fieldSelector.Body });
var whereExpression = Expression.Lambda<Func<TTable, bool>>(callContains, (fieldSelector.Body as MemberExpression).Expression as ParameterExpression);
return table.Where(whereExpression);
}
You should be able to remove the table parameter I used and use the member variable you have.
Instead of Expression<Func<ZipCode, T>> field use Func<ZipCode, T> field:
public IEnumerable<ZipCode> match<T>(IEnumerable<T> values, Func<ZipCode, T> field)
{
HashSet<T> valueSet = new HashSet<T>(values);
return _table.Where(x => valueSet.Contains(field(x)));
}