I am new to c# and I am trying to print a user defined structure. Code:
var tracksArr = new[]
{new
{ vessel = GetVesselInfo(aisRecord.VesselId),
points = new[]
{ new
{ stampUtc = aisRecord.Time.ToString("yyyy-MM-ddTHH:mm:ssZ"),
}}}}
foreach (var item in tracksArr)
{Console.WriteLine("qqq: " + item.ToString());}
which prints:
qqq: { vessel = { key = 123456,0, mmsi = 7891011, imo = 0 }, points =
<>f__AnonymousType18`6[System.String,System.Double,System.Double...
what is this mysterious <>f__AnonymousType18 and how do I get the value of points?
For each anonymous type with unique set of fields (the new { ... } statements in means creation of instance of anonymous type) compiler will generate a class, which name will look like <>f__AnonymousType18. This class has overridden ToString method, but arrays/collections - don't and point is an array, so by default ToString returns type name which is YourAnonymousTypeName[] for arrays. You can use string.Join to output your collection:
Console.WriteLine($"qqq: {{vessel = {item.vessel}, points = {string.Join(", ", item.points.Select(p => p.ToString()))}}}");
Or create/use another collection type for points which will have overridden ToString method which returns string with all elements:
public static class ext
{
public static MyList<T> ToMyList<T>(this T[] arr)
{
return new MyList<T>(arr);
}
}
public class MyList<T> : List<T>
{
public MyList(T[] arr)
{
AddRange(arr);
}
public override string ToString()
{
return string.Join(",", this);
}
}
var tracksArr = new[]
{new
{ vessel = 1,
points = new[]
{ new
{ stampUtc = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ"), }
}
.ToMyList()
}
}; // prints "qqq: { vessel = 1, points = { stampUtc = 2020-06-24T14:58:08Z } }"
Related
I want this method to work with any variable - i.e., passing a "Price" value to the method then getting the total price of all items.
private int GetTotalValue(int stat){
int total = 0;
foreach(Item i in Vendor.items){
totalStat += i.stat;
}
return total;
}
However, it has no way of knowing the name of the variable that I passed as the parameter, and thus no way of accessing it in other objects.
How would I tell the method what variable I'm passing, instead of just the value of it?
If you always want the sum of some property value you could encapsulate that logic into a method, e.g. GetVendorItemSum:
internal class Program
{
private static void Main(string[] args)
{
var items = new[] {
new Item {Price = 1},
new Item {Price = 2}
};
var vendor = new Vendor {Items = items};
var vendorSum = GetVendorItemsSum(vendor, x => x.Price);
}
private static int GetVendorItemsSum(Vendor vendor, Func<Item, int> func)
{
return vendor.Items.Sum(func);
}
}
public class Vendor
{
public IEnumerable<Item> Items;
}
public class Item
{
public int Price { get; set; }
}
in the code below calling SequenceEqual on generic list return true (as expected) when List is defined with class generic type (EquatableClass.Equals<> is called).
If list is defined with IEquatable interface, Equals method is not called and result is false (object.Equals is called instead, not in code).
The question is, why the EquatableClass.Equals<> method is not called in the second case?
public class EquatableClass : IEquatable<EquatableClass>
{
public string Name { get; set; }
public bool Equals(EquatableClass other) => this.Name.Equals(other.Name);
}
static void Main(string[] args)
{
var A = new List<EquatableClass> { new EquatableClass { Name = "A" } };
var B = new List<EquatableClass> { new EquatableClass { Name = "A" } };
var result1 = A.SequenceEqual(B); // == true;
var AA = new List<IEquatable<EquatableClass>> { new EquatableClass { Name = "A" } };
var BB = new List<IEquatable<EquatableClass>> { new EquatableClass { Name = "A" } };
var result2 = AA.SequenceEqual(BB); // == false;
}
The SequenceEqual<T> method will try to see if it can convert T to IEquatable<T>, and if it can, it will use IEquatable<T>.Equals for equality.
When you have a List<EquatableClass> it will then try to convert EquatableClass to IEquatable<EquatableClass>, and that succeeds, so it uses the appropriate Equals method.
When you have a List<IEquatable<EquatableClass>> it will then try to convert IEquatable<EquatableClass> to IEquatable<IEquatable<EquatableClass>>, and that will fail, because the actual object doesn't implement IEquatable<IEquatable<EquatableClass>>, so it resorts to the default behavior of using object.Equals(object), which you don't override.
I have a class baseClass, and a list of objects of the baseClass. What i want to achieve is that i have to dynamically assign the instance number to each object in the list. for that what am doing is that use a constructor to do this.
Following is the class definition:
public class baseClass
{
private int _InstanceNumber;
private int _MyIntVal;
private string _MyString;
public string MyString
{
get { return _MyString; }
set { _MyString = value; }
}
public int MyIntVal
{
get { return _MyIntVal; }
set { _MyIntVal = value; }
}
public int MyProperty
{
get { return _InstanceNumber; }
}
public baseClass(int instance)
{
_InstanceNumber = instance;
}
}
The creation of the List of objects is as follows:
int instanceNumber = 0;
List<baseClass> classList = new List<baseClass>();
classList.Add(new baseClass(instanceNumber++) { MyString = "sample1", MyIntVal = 10 });
classList.Add(new baseClass(instanceNumber++) { MyString = "sample2", MyIntVal = 11 });
I know it is not the actual way for creating this. it does not give the index number actually. how can i calculate the instance number?
Consider the following scenario, that am creating another list of objects then it hard to maintain the instance number. or if i create another object(this also be an instance) external to the list.
int instanceNumber = 0;
List<baseClass> anotherClassList = new List<baseClass>();
classList.Add(new baseClass(instanceNumber++) { MyString = "sample1", MyIntVal = 10 });
classList.Add(new baseClass(instanceNumber++) { MyString = "sample2", MyIntVal = 11 });
Updates:
This is my temporary solution for this. i need proper way/ method to maintain instance number
If you want to find the index of item in the list, you should ask it from the list, not the item like:
var index = list.IndexOf(item);
But it seems that you expect the item to be aware of its position in the list. In order to do this, you should pass the list to the item so it can use it to find its own place in it:
public class Item
{
private List<Item> _containerList;
public Item(List<Item> containerList)
{
_containerList = containerList;
}
public int InstanceNumber
{
get { return _containerList.IndexOf(this); }
}
}
and change your code to:
List<Item> classList = new List<Item>();
classList.Add(new Item(classList ) { ... });
classList.Add(new Item(classList ) { ... });
I have this class:
public class allFields
{
public string EAN { get; set; }
public string title { get; set; }
public string qty { get; set; }
public string price { get; set; }
public DateTime date { get; set; }
}
And a function that return an anonymous type:
public IEnumerable<object> stockEtatQty()
{
List<allFields> afList = new List<allFields>();
var query = from x in ctx.book
where x.qty > 0
select x;
foreach (var item in query)
{
allFields af = new allFields();
af.EAN = item.EAN;
af.title = item.Titre;
af.qty = ""+item.Quantite;
afList.Add(af);
}
var q = from x in afList
select new { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q; //q is a IEnumerable<'a> where a is new {string EAN, string Title, string Quantity}
}
In my WinForm a use this function as below:
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty().ToList();// q is a list<object>
string str = "";
foreach (var item in q)
{
str += item + Environment.NewLine;
}
MessageBox.Show(str);
}
The result is:
{ EAN = 1, Title = CSharp Security, Quantity = 970 }
{ EAN = 2, Title = MISC, Quantity = 100 }
...
What I want?
I want not like the result above, but separate each field apart of the item in the loop foreach, e.g get item.EAN, item.Title and item.Quantity.
If there is no solution for my problem I would like to know an alternative,
Thanks for help.
The obvious solution is to create a custom type (let's call it BookInfo) and return a IEnumerable<BookInfo> instead of a IEnumerable<object> (and maybe override ToString if you want to put the formatting into this class itself).
Then you can easily format the output.
public class BookInfo
{
public string EAN {get;set;}
public string Title {get;set;}
public int Quantity {get;set;}
}
public IEnumerable<BookInfo> stockEtatQty()
{
...
var q = from x in afList
select new BookInfo { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q;
}
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty();
var message = string.Join(Environment.NewLine,
q.Select(item => String.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)));
MessageBox.Show(message);
}
Since the static type information about the object of anonymous type is lost by the time that you exit stockEtatQty() method, you could cast the object to dynamic and access fields like this:
str = string.Join(Environment.NewLine, q.Cast<dynamic>().Select(item =>
string.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)
));
The cast to dynamic tells the compiler that EAN, Title, and Quantity need to be resolved at runtime.
Note that I also replaced the foreach loop with a call to string.Join to improve performance: repeated string concatenation creates unnecessary partial string objects, which string.Join avoids. Another solution would be to use StringBuider instead of string concatenation +=.
stockEtatQty is in a project (Service) and QuantityToolStripMenuItem_Click is in another project (View)
Unfortunately, this means that you would not be able to use anonymous types: anonymous types are generated with internal visibility, limiting their use to the assembly in which they are produced. You can use a work-around based on ExpandoObject described in this answer:
var q = afList.Select(x => {
dynamic res = new ExpandoObject();
res.EAN=x.EAN;
res.Title=x.title;
res.Quantity=x.qty;
return res;
});
Create a new class that represents the new object structure and return that.
var q = from x in afList
select new SmallerType { EAN=x.EAN, Title=x.title, Quantity=x.qty };
WinForm Function
foreach (SmallerType item in q)
{
//
}
You can use collection of dynamic objects instead of simple objects as return type of your method:
public IEnumerable<dynamic> stockEtatQty()
Then you will not have IntelliSense but at runtime properties will be found:
foreach (var item in sstock.stockEtatQty())
str += String.Format("{0}", item.EAN) + Environment.NewLine;
But I suggest you to create custom class with EAN, Title and Quantity properties. Or just use your allFields instead of anonymous objects.
Consider also to use StringBuilder for string creation to avoid creating lot of in-memory strings:
var builder = new StringBuilder();
foreach (var item in sstock.stockEtatQty())
builder.AppendFormat("{0}{1}", item.EAN, Environment.NewLine);
MessageBox.Show(builder.ToString());
For an application I'm working on I'm trying to display a template that will show what the parameters for a (runtime-determined) method look like. The test case I'm working on should show "PERSON = (FIRST = first; LAST = last);", where the parameter named Person has type Name, and Name has two properties, First and Last. The following code instead shows "PERSON = ();".
GetNestedTypes is not returning anything, any ideas why?
public static string GetParameterTemplate(MethodInfo method)
{
StringBuilder output = new StringBuilder();
foreach (ParameterInfo pi in method.GetParameters())
{
output.Append(parameterTemplateHelper(pi.Name, pi.ParameterType));
}
return output.ToString();
}
private static string parameterTemplateHelper(string pName, Type pType)
{
string key = pName.ToUpper();
string value = "";
if (pType.IsPrimitive)
{
// it's a primitive
value = pName.ToLower();
}
else if (pType.IsArray)
{
if (pType.GetElementType().IsPrimitive)
{
// array of primitives
value = String.Format("{0}1, {0}2;", pName.ToLower());
}
else
{
// array of objects
StringBuilder sb = new StringBuilder();
foreach (Type t in pType.GetElementType().GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))
{
sb.Append(parameterTemplateHelper(t.Name, t));
}
value = String.Format("({0}), ({0});", sb);
}
}
else
{
// object
StringBuilder sb = new StringBuilder();
Type[] junk = pType.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
foreach (Type t in pType.GetNestedTypes())
{
sb.Append(parameterTemplateHelper(t.Name, t));
}
value = String.Format("({0});", sb.ToString());
}
string output = key + " = " + value.ToString();
return output;
}
Your code is looking for nested types - that is, other types declared within Person. That's not at all the same as looking for properties within Person.
Here's a class with nested types:
public class Name
{
public class Nested1 {}
public class Nested2 {}
}
Here's a class with properties:
public class Name
{
public string Name { get; set; }
public string Name { get; set; }
}
My guess is that your situation is much more like the second one than the first... so use Type.GetProperties instead of Type.GetNestedTypes.