I have this class :
public class User
{
public int Id;
public ICollection<Role> Roles;
}
And I have this object :
User userObj = new User { Id = 1 };
I want set this array :
RolesVM.Items.Split(new string[] { "\r\n" }, StringSplitOptions.None);
For :
userObj.Roles
When assign the string[] array to the Roles, I'm getting this error:
Cannot implicitly convert type 'string[]' to 'System.Collections.Generic.ICollection
How can I do this ?
There is no magical way to transform string to other type. You need to call code that creates objects of that type somehow.
I.e. if role have constructor taking a string argument:
userObj.Roles =
RolesVM.Items.Split(new string[] {"\r\n" }, StringSplitOptions.None)
.Select(value => new role(value)) // "convert" strings to `role`
.ToList(); // transform enumerable to type that implements ICollection (List)
Depending on your role type new role(value) may need to be replaced with other conversion like new role{ Value = value} or for enums - Enum.Parse(typeof(role), value).
Related
I have the same situation as this user how to use string.join to join value from an object array? in this question. However, I want to join all values from the object instead of only 1 value.
To recap my question:
I have an array of object e.g:
MyObject[] objs;
and within MyObject it contains 3 string property,
object[0].stringValue1
object[0].stringValue2
object[0].stringValue3
If I want to join the whole array of objects by all of their stringValues (stringValues1,2 and 3), how can I do it?
I think selector doesn’t allow me to select several elements, then how to use string.join to join several values from an object array?
See below for example usage of the two extension methods provided in the implementation section below. The first allows you to select the properties to include, and the second includes all string properties of the object in the source collection which I believe is the exact answer to your question.
Example Usage
Note, the resulting output from the two examples below are ordered differently as a result of how each implementation works, however the results are otherwise identical as a result of the first example specifying all string properties on the MyObj sample type
Live Fiddle Example
// Test Object
public class MyObj
{
public MyObj(string prop1, string prop2, string prop3)
{
Prop1 = prop1;
Prop2 = prop2;
Prop3 = prop3;
}
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
// Sample Data
var list = new List<MyObj> {
new MyObj("A1", "B1", "C1"),
new MyObj("A1", "B2", "C2"),
new MyObj("A3", "B3", "C3")
};
Samples using above object and data
// Example #1 - All properties separated by single separator
Console.WriteLine(list.Join(colSeparator: ','));
// RESULT: A1,A1,A3,B1,B2,B3,C1,C2,C3
// Example #2 - Object property separator, and different object separator
Console.WriteLine(list.Join(colSeparator: ',', rowSeparator: '\n'));
// RESULT: A1,B1,C1
A1,B2,C2
A3,B3,C3
Implementation
public static class EnumerableStringJoinExtension
{
public static string Join<T>(this IEnumerable<T> values, char colSeparator, char? rowSeparator = null)
{
var strProperties = typeof(T).GetProperties().Where(r=>r.PropertyType == typeof(string));
var sb = new StringBuilder();
foreach(var val in values)
sb.Append(string.Join(colSeparator, strProperties.Select(r=> r.GetValue(val)))).Append(rowSeparator ?? colSeparator);
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
}
A possible way to solve it is to first create an array of each object's properties (using .Select()), and then flatten the resulting property arrays by using .SelectMany(). Both of those methods are found in the System.Linq namespace. The resulting IEnumerable<string> object's items can then be joined using string.Join().
If MyObject is defined as
class MyObject
{
public string First { get; set; }
public string Second { get; set; }
public string Third { get; set; }
}
and you define objects as
List<MyObject> objects = new()
{
new() { First = "ABC", Second = "DEF", Third = "GHI" },
new() { First = "JKL", Second = "MNO", Third = "PQR" },
new() { First = "STU", Second = "VWX", Third = "YZ" },
};
, a possible implementation is:
var flattenedObjects = objects
.Select(obj => new[] { obj.First, obj.Second, obj.Third })
.SelectMany(properties => properties);
var objectString = string.Join("_", flattenedObjects);
Printing the value of objectString to the console gives:
ABC_DEF_GHI_JKL_MNO_PQR_STU_VWX_YZ
Example fiddle here.
If you just want a one-line, handy C# without hard-coding property name you could try this:
string.Join("\n", objs.Select(o => string.Join("_", o.GetType().GetProperties().Select(p => p.GetValue(o)))));
This produces the following result:
"A_B_C\nD_E_F\nG_H_I"
with the object array as:
var objs = new MyObject[]
{
new MyObject("A", "B", "C"),
new MyObject("D", "E", "F"),
new MyObject("G", "H", "I")
};
But please note that if your class has properties in other types then you might need a Where() between GetProperties() and the second Select() to exclude unwanted properties.
Plz help with System.Linq.Aggregate
I have the following class
public class MainClass
{
public ClassA someProp { get; set; }
public ClassA someProp2 { get; set; }
}
public class ClassA
{
public virtual Type Types => Type.None;
}
public class ClassB:ClassA
{
public override Type Types => Type.Default;
public string FieldName { get; set; }
}
public class ClassC:ClassA
{
public override Type Types => Type.Value;
public int FieldValue { get; set; }
}
And i want to get FieldName if it is filled in ClassB or the value from ClassC if it is also filled for someProp2 and someProp
I understand how to get 1 value
//for get name
IEnumerable<string> values = entities
.Where(x=>x.someProp!=null || x.someProp2!=null)
.SelectMany(mainClass => new[] {mainClass.someProp,mainClass.someProp2 })
.OfType<ClassB>()
.Select(classB => classB.FieldName)
//for get value
IEnumerable<int> values = entities
.Where(x=>x.someProp!=null || x.someProp2!=null)
.SelectMany(mainClass => new[] {mainClass.someProp,mainClass.someProp2 })
.OfType<ClassC>()
.Select(classC => classC.FieldValue)
but I don’t understand how to get 2 values in 1 requests, because there will be type 2 classes ClassB and ClassC
Probably the best way would be through the Aggregate method!
Tell me how to make the conditions for the selection and the Aggregate method itself
If you want to use .Aggregate() to achieve your goal, I would suggest that you use an overload that lets you define a seed in addition to the accumulator function after flattening the property pairs.
It could be implemented as follows:
IEnumerable<string> values = entities
.SelectMany(ent => new[] { ent.someProp, ent.someProp2 })
.Aggregate(new List<string>(),
( fields, property ) =>
{
if (property is ClassB)
{
fields.Add(((ClassB)property).FieldName);
}
else if (property is ClassC)
{
fields.Add(((ClassC)property).FieldValue.ToString());
}
return fields;
});
Here, our seed is
new List<string>()
and our accumulator function is
( fields, property ) =>
{
// accumulation logic
return fields;
}
, containing the accumulator value parameter fields and element parameter property.
Our seed is the initial value of our accumulator value. For the first property in the flattened property collection (provided by the .SelectMany()), our accumulator value (fields) is therefore an empty list of strings.
For each element (property), field values are extraced based on the property's class type:
if property is a ClassB, FieldName is extracted and added to our accumulator value.
if property is a ClassC, FieldValue is extracted, and its string value is added to our accumulator value.
(if property is neither of those classes, nothing is added to fields).
After conditionally adding a field value to fields, fields is returned from the accumulator function to be used as the accumulator value in the next iteration of the accumulator function (i.e. for the next property in the flattened collection).
For entities given as follows:
List<MainClass> entities = new()
{
new()
{
someProp = new ClassC() { FieldValue = 4 },
someProp2 = new ClassB() { FieldName = "Flower" }
},
new()
{
someProp = new ClassA() { },
someProp2 = new ClassC() { FieldValue = 7 }
}
};
, the resulting values will contain:
4
Flower
7
Example fiddle here.
How and "could be" organized return from the method which returns tuple type with the name of parameters,
as an example
private static Tuple<string, string> methodTuple()
{
return new {Name = "Nick", Age = "Twenty"}; /*exception because need to new Tuple<string, string>(){Item1 = "Nick", Item2 = "Twenty"}o*/
}
and call parameters like methodTuple.Name not like methodTuple.Item1....N
Is this possible or not?
UPD: I want to create object with named parameters without new named type.
In C# 7.0 (Visual Studio 2017) there is a new option to do that:
(string first, string middle, string last) LookupName(long id)
Starting C# v7.0, it is now possible to give custom name to tuple properties. Earlier they used to have default names like Item1, Item2 and so on. Let's look at few variations which is now possible:
Naming the properties of Tuple Literals:
var personDetails = (Name: "Foo", Age: 22, FavoriteFood: "Bar");
Console.WriteLine($"Name - {personDetails.Name}, Age - {personDetails.Age}, Favorite Food - {personDetails.FavoriteFood}");
The output on console:
Name - Foo, Age - 22, Favorite Food - 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# which was introduced in version 6 as detailed here.
You need to declare a helper class to do so.
public class MyResult
{
public string Name { get; set; }
public string Age { get; set; }
}
What you're trying to return is an anonymous type. As the name suggests you don't know what its name is, so you can't declare your method to return it.
Anonymous Types (C# Programming Guide)
You cannot declare a field, a property, an event, or the return type
of a method as having an anonymous type. Similarly, you cannot declare
a formal parameter of a method, property, constructor, or indexer as
having an anonymous type. To pass an anonymous type, or a collection
that contains anonymous types, as an argument to a method, you can
declare the parameter as type object. However, doing this defeats the
purpose of strong typing. If you must store query results or pass them
outside the method boundary, consider using an ordinary named struct
or class instead of an anonymous type.
Update
C#7 introduces Tuple support built into the language and it comes with named tuples
(string name, int age) methodTuple()
{
(...)
}
Read more on learn.microsoft.com: https://learn.microsoft.com/en-us/dotnet/articles/csharp/csharp-7#tuples
This is not possible with Tuple, no. You'll need to create your own new named type to do this.
Now you can do it with tuple Name in C#
For Lambda Expression:
private static (string Name, string Age) methodTuple() => ( "Nick", "Twenty" );
Or
private static (string Name, string Age) methodTuple()
{
return ( "Nick", "Twenty" );
}
Do not use class type for Tuple. Use primitive type to set the name in Tuple.
I usually create a new type that derives from Tuple, and map your explicit properties to return the base class's ItemX properties.
eg:
public class Person : Tuple<string, string>
{
public Key(string name, string age) : base(name, age) { }
public string Name => Item1;
public string Age => Item2;
}
Unfortunately, this is not possible using the "Tuple" type, as it is defined as "Item1...N" in MSDN. So this exception is valid.
This method can compile in 3 ways:
1.) Change return type to object - this will create an "anonymous" type, which you can then use later. It is not particularly useful if you want to access the "Name" or "Age" property later without some additional work.
2.) Change return type to dynamic - this will let you access the "Name" and "Age" property, but will make the entire program (just the DLL where this method is located really) slightly slower as the use of dynamic necessitates throwing out some strong typing.
3.) Create a class and use it as teh return type.
Sample code here:
private static object ObjectTuple()
{
return new { Name = "Nick", Age = "Twenty" };
}
private static dynamic DynamicTuple()
{
return new { Name = "Nick", Age = "Twenty" };
}
private static Temp TempTuple()
{
return new Temp{ Name = "Nick", Age = "Twenty" };
}
class Temp
{
public string Name { get; set; }
public string Age { get; set; }
}
As per me, when you want to return or get many things from a single method, better make its return type as CLASS but if you intend to use Tuple which itself is Class then for better naming this new class should inherit from Tuple. e.g. mentioned below.
public CustomReturn ExecuteTask( int a, string b, bool c, object d )
{
// Calling constructor of CustomReturn Class to set and get values
return new CustomReturn(a,b,c,d);
}
internal class CustomReturn
// for tuple inherit from Tuple<int,string,bool,object,double>
{
//for tuple public int A{ get {this.Item1} private set;}
public int A{get;private set;}
public string B{get;private set;}
public bool C{get;private set;}
public object D{get;private set;}
public CustomReturn (int a, string b, bool c, object d )
// use this line for tuple ": base( obj, boolean )"
{
this.A = a;
this.B = b;
this.C = c;
this.D = d;
}
}
Main(args)
{
var result = ExecuteTask( 10, "s", true, "object" );
// now if u have inherited Tuple for CustomReturn class then
// on doing result. you will get your custom name as A,B,C,D for //Item1,Item2,Item3,Item4 respectively also these Item1,Item2,Item3,Item4 will also be there.
}
In class below, I would like to sometimes pass just an array of strings for the property 'BodyParameters'.
Is that possible without using object type as its parameter type? Most times, this property will be a List of string [ ] arrays when the class is used.
public class EmailTemplate
{
...
public IList<string[]> BodyParameters { get; set; }
...
}
If you want to set BodyParameters using only a single string[], you can do this:
string[] value = ...;
myEmailTemplate.BodyParameters = new [] { value };
There is no implicit conversion from T to IList<T>, where T is string[] in your case.
The above code will work because new [] { ... } will infer the type string[][], which implements IList<string[]>.
I have an ICollection with objects:
private ObservableCollection<ViewItem> items;
The viewItems have no properties. The data will be accessed via an index with
public object this[int index] {
get{ .... }
set {....}
}
I have a geneal class for filtering. The linq with properies will work fine. I use (the important code only):
Queryable = CreateQueryable((IEnumerable<object>)mItemsSource.SourceCollection, ItemType);
mQuery = Queryable.Where(filterString).Cast<object>();
ilteredCollection = mQuery.ToList();
with:
private static IQueryable CreateQueryable(IEnumerable<object> collection, Type itemType)
{
if (itemType == null) return null;
var queryableList = collection.AsQueryable();
return queryableList.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Cast",
new Type[] { itemType },
queryableList.Expression));
}
So I can use a filter string like: Id>10 or Name="abc"
where Id and Name are property names.
But I have also Object in another collection which only have access via index. so I have an where string like:
[0]>10 or [1]="abc"
I didn't find any solution. The only hint I could find is to use:
new(it([idx] as Type)
where idx is element index and Type is a type of this element
e.g.
[0]>10 --> new(it[0] as object)>10
But than I get the error:
{"Operator '=' incompatible with operand types 'DynamicClass1' and 'Int32'"}
Useing a string in my filter like:
new(it[0] as object)>"10"
than the error is:
{"Operator '=' incompatible with operand types 'DynamicClass1' and 'string'"}
So - how can I solve this problem. Because this is a general Filterclass I also don't know the type. So in the as statement I can only use object or something like this.
I hope anyone can help me. Perhaps the dynamic keyword of C# 4.0 will help??
BTW a workaround will be to impement a wrapper in each class with indexer, but this will be a lot of stupid work. And that is something a real programmer don't like ;). I am sure there is a solution!
Cheer up !!
First of all -- How to access Current Instance ?
When parsing a lambda expression with a single unnamed parameter, the members of the unnamed parameter are automatically in scope in the expression string, and the current instance given by the unnamed parameter can be referenced in whole using the keyword it. For example,
customers.Where("Country = #0", country);
is equivalent to
customers.Where("it.Country = #0", country);
Above concept has been explained here.
From above explanation, we can now access the indexer property as it[#0] where #0 is value of index to be passed, as explained below.
//Consider below class
public class Product
{
private NameValueCollection collection = new NameValueCollection();
public string Company { get; set; }
public string Distributor { get; set; }
public int ID { get; set; }
...
public string this[string index]
{
get { return collection[index]; }
set { if(!string.IsNullOrEmpty(value)) collection[index]=value; }
}
}
//Main Code
List<Product> list = new List<Product>();
Product product = new Product() { Company = "Nestle", Distributor = "xyz", ID = 1 };
product["Name"] = "Maggi";
list.Add(product);
var filteredList = list.AsQueryable().Where("it[#0]=#1", "Name", "Maggi"); //Accessing the current item by indexer property
foreach (Product productItem in filteredList)
{
Console.WriteLine(productItem.Company);
}
Hope this helps you !! :)
Your usage of new keyword is wrong.
It does not cast object (nor does as).
Keyword new is used to create new object of anonymous class with the specified properties.
Thus new(it[idx] as Type) will create new object with property Type having the value it[idx]. It is equivalent to C#'s: new { Type = this[idx] }.
As I have already pointed out in Dynamic linq: Is there a way to access object data by index?, you need to cast it in the following manner: Int32(it[0]) > 10 for your pseudo-query [0] > 10.