As explained in https://learn.microsoft.com/de-de/visualstudio/code-quality/ca2227?view=vs-2019 I've got an object with a read only list like this:
public class MyClass {
public int id { get; set; }
public List<string> stringList { get; } = new List<string>;
}
But how can I initialize MyClass by adding data to stringList?
MyClass test = new MyClass(){
id = 1,
stringList = ???
}
You can use not very obvious syntax with collection initializers:
var x = new MyClass
{
id = 1,
stringList = {"as", "ddsd"} // will ADD "as", "ddsd" to stringList
};
Console.WriteLine(string.Join(", ", x.stringList)); // prints as, ddsd
Bur usual approach to handle readonly properties (until C# 9 is released with init only properties and records) is to pass initialization values in constructor.
You can pass the stringList as a parameter in the constructor and assign the property to the parameter:
public class MyClass {
public int id { get; set; }
public List<string> stringList { get; } = new List<string>();
public MyClass(List<string> stringList) {
this.stringList = stringList;
}
}
Related
Consider:
public interface I
{
int InterfaceProperty {get;set;}
}
public class C1 : I
{
public int InterfaceProperty {get;set;}
public int Class1Property {get;set;}
}
public class C2 : I
{
public int InterfaceProperty {get;set;}
public int Class2Property {get;set;}
}
//In some other class:
public List<I> L;
void somemethod()
{
this.L = new List<I>();
this.L.Add(new C1()); //add some C1s into the list
SomeMethodToGetProperties(L);
this.L = new List<I>();
this.L.Add(new C2()); //add some C2s into the list
SomeMethodToGetProperties(L);
}
I need SomeMethodToGetProperties that gets a list of the properties for C1 or C2. ie, first call returns InterfaceProperty and Class1Property and the second call returns InterfaceProperty and Class2Property.
I can't use an object in the list, because the lists may be empty. I tried Reflection on the lists, but that only gave me the properties for the interface.
EDIT: The original way I wrote it was not valid. You can't do
this.L = new List<C1>()
You can only do something like
this.L = new List<I>();
this.L.Add(new C1());
It seems what I need may not be possible from the metadata of the list itself.
So I created a second variable to hold the type of item held in the list that I set every time I change the list contents.
This is one implementation, that scans each item in a list, and collects the item type and its properties. This is because each list might have more than one type that is inherited from the interface.
I renamed the interface and classes for clarity.
The result is
Class1
InterfaceProperty
Class1Property
Class2
InterfaceProperty
Class2Property
with code:
public interface IInterface
{
int InterfaceProperty { get; set; }
}
public class Class1 : IInterface
{
public int InterfaceProperty { get; set; }
public int Class1Property { get; set; }
}
public class Class2 : IInterface
{
public int InterfaceProperty { get; set; }
public int Class2Property { get; set; }
}
class Program
{
public static List<IInterface> list;
static void Main(string[] args)
{
list = new List<IInterface>();
list.Add(new Class1());
list.Add(new Class2());
list.Add(new Class1());
list.Add(new Class1());
list.Add(new Class2());
foreach (var itemType in GetItemTypeProperties(list))
{
Console.WriteLine(itemType.Key.Name);
foreach (var property in itemType.Single())
{
Console.WriteLine($"\t{property.Name}");
}
Console.WriteLine();
}
}
public static IEnumerable<IGrouping<Type,PropertyInfo[]>> GetItemTypeProperties<T>(List<T> list)
{
var itemProperties = new Dictionary<Type, PropertyInfo[]>();
foreach (var item in list)
{
var t = item.GetType();
if (!itemProperties.ContainsKey(t))
{
itemProperties[t] = t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
}
}
return itemProperties.GroupBy(kv => kv.Key, kv=>kv.Value);
}
}
You can also get the same result, by scanning only the first item in the list and assuming the remaining items are of the same time. This time, like in your question, I re-assign the list with a new set of items of a different type.
This produces the following result:
Class1
InterfaceProperty
Class1Property
Class2
InterfaceProperty
Class2Property
from the code:
list = new List<IInterface>();
list.Add(new Class1());
list.Add(new Class1());
list.Add(new Class1());
var item1 = list.First();
var properties1 = item1.GetType().GetProperties();
Console.WriteLine($"{item1.GetType().Name}");
foreach (var prop in properties1)
{
Console.WriteLine($"\t{prop.Name}");
}
list = new List<IInterface>();
list.Add(new Class2());
list.Add(new Class2());
var item2 = list.First();
var properties2 = item2.GetType().GetProperties();
Console.WriteLine($"{item2.GetType().Name}");
foreach (var prop in properties2)
{
Console.WriteLine($"\t{prop.Name}");
}
To get the actual type of the list:
Type listType = this.L.GetType();
To get the types of objects it can contain:
Type elementType = listType.GetGenericArguments().Single();
To get the properties for that type:
var properties = elementType.GetProperties();
I was wondering if there was a method in which I can hold a generic list that can hold a generic list.
My issue is that I have a list that can hold either LIST A or LIST B or LIST C.
I figured out how to do this with Data types but I want this list to be able to hold classes that I create.
For example:
List<T> listA = new List<T>();
Where T is ObjectX
listA.Add(new list<Y> { new List<U>() { new List<T>() } } );
Where Y is ObjectY<br>
Where U is ObjectU
etc.
EDIT:
Let me put it into context.
I have a list of Objects called Suites
Each Suite can have a list of CaseObjects OR a list of CaseHolderObjects.
Each CaseHolder can hold a list of CaseObjects
Each Case can hold a list of ActionObjects
I think this is what you want:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace TestList
{
public class Program
{
public static void Main(string[] args)
{
//Use this syntax for a list of list of classes
List<List<Test>> test = new List<List<Test>>();
}
}
//This class is just for example.
class Test
{
//Your class code here
}
}
This is what you wanted, a list that holds any other type of list ^^
class Program
{
static void Main(string[] args)
{
Person p1 = new Person(1, "Mario");
Person p2 = new Person(2, "Franco");
Dog d1 = new Dog(1, "Fuffy");
Dog d2 = new Dog(2, "Chop");
List<Person> listP = new List<Person>();
listP.Add(p1);
listP.Add(p2);
List<Dog> listD = new List<Dog>();
listD.Add(d1);
listD.Add(d2);
List<Object> listO = new List<Object>();
listO.Add(listP);
listO.Add(listD);
}
public class Person
{
public Person(int id, string name)
{
this.id = id;
this.name = name;
}
public int id { get; set; }
public string name { get; set; }
}
public class Dog
{
public Dog(int id, string name)
{
this.id = id;
this.name = name;
}
public int id { get; set; }
public string name { get; set; }
}
}
I solved the issue. The classes in question I had them implement an empty interface. I then created a single property.
List<List<Interfaces.IMetaData>> myList = new List<List<Interfaces.IMetaData>>();
myList.Add(new List<Interfaces.IMetaData>() { new SuiteObject() { } });
myList.Add(new List<Interfaces.IMetaData>() { new CaseHolderObject() { } });
myList.Add(new List<Interfaces.IMetaData>() { new CaseObject() { } });
myList.Add(new List<Interfaces.IMetaData>() { new ActionObject() { } });
You don't need generic collection if you want to store different class in it. Try to use ArrayList (System.Collections namespace). You can add to it any object (of cource cost of performace);
For example:
ArrayList listA = new ArrayList() { 1, true, "string" };
ArrayList ListB = new ArrayList() { 2, false };
ArrayList ListC = new ArrayList() { 3, "string3" };
ListB.Add(ListC);
listA.Add(ListB);
I am trying to update a List which is a List of Interfaces to concrete classes.
I add to the List each Market type i am interested in, for this Example these Markets are A and B
I loop over all the markets, (sample provided with 3 markets A B & C, we are only interested in A and B) And determine which is of interest to us.
Once found we pass this to an extraction method too do its work and create an instance of the Correct Market_ class type.
This all works fine, but when i try to update the list with the Updates it does not get reflected in the List.
Code below, any Suggestions?
Thanks
public class Test
{
public Test()
{
TheMarkets MarketsToUpdate = new TheMarkets();
List<SpecificCompanyMarket> lstMarks = new List<SpecificCompanyMarket>();
lstMarks.Add(new SpecificCompanyMarket(1234, "A", "Some HTML DATA HERE"));
lstMarks.Add(new SpecificCompanyMarket(5874, "B", "Some HTML DATA HERE"));
lstMarks.Add(new SpecificCompanyMarket(2224, "C", "Some HTML DATA HERE"));
foreach (var item in lstMarks)
{
if (MarketsToUpdate.IsMarketWeAreInterestedIn(item.MarketName))
{
ITheMarkets MarkToUpdate = ExtractMarketData(item);
var obj = MarketsToUpdate.MarketsWeAreInterestedIn.FirstOrDefault(x => x.MarketName() == "A");
if (obj != null)
{
obj = MarkToUpdate;
}
}
}
//Look At MarketsToUpdate Now and the item has not changed, still original values
//I was expecting to see the new values for the fields in A, not the default 0's
}
public ITheMarkets ExtractMarketData(SpecificCompanyMarket item)
{
ITheMarkets market = null;
if (item.MarketName.ToUpper() == "A")
{
Market_A marketType = new Market_A();
marketType.SomeValue1 = 123;
marketType.SomeValue2 = 158253;
market = marketType;
}
//Other Market extractions here
return market;
}
}
public class SpecificCompanyMarket
{
public int MarketId { get; set; }
public string MarketName { get; set; }
public string MarketDataHTML { get; set; }
public SpecificCompanyMarket(int MID, string MName, string MData)
{
MarketId = MID;
MarketName = MName;
MarketDataHTML = MData;
}
}
public class TheMarkets
{
public List<ITheMarkets> MarketsWeAreInterestedIn = new List<ITheMarkets>();
public TheMarkets()
{
Market_A A = new Market_A();
Market_B B = new Market_B();
MarketsWeAreInterestedIn.Add(A);
MarketsWeAreInterestedIn.Add(B);
}
public bool IsMarketWeAreInterestedIn(string strMarketName)
{
bool blnRetVal = false;
foreach (var item in MarketsWeAreInterestedIn)
{
if (item.MarketName().ToUpper().Trim().Equals(strMarketName.ToUpper().Trim()))
{
blnRetVal = true;
break;
}
}
return blnRetVal;
}
}
public interface ITheMarkets
{
string MarketName();
}
public class Market_A : ITheMarkets
{
public string LabelType { get; private set; }
public double SomeValue1 { get; set; }
public double SomeValue2 { get; set; }
public double SomeValue3 { get; set; }
public Market_A()
{
LabelType = "A";
}
public string MarketName()
{
return LabelType;
}
}
public class Market_B : ITheMarkets
{
public string LabelType { get; private set; }
public List<string> SomeList { get; set; }
public double SomeValue { get; set; }
public Market_B()
{
LabelType = "B";
}
public string MarketName()
{
return LabelType;
}
}
This is a short example to get you going. Loop through your list, find the object you want to update, create a new object of that type and then find the original objects index in the list and overwrite it in place. You are essentially just replacing the object in the list with a new one not mutating the existing one.
foreach (var item in lstMarks)
{
//your code to get an object with data to update
var yourObjectToUpdate = item.GetTheOneYouWant();
//make updates
yourObjectToUpdate.SomeProperty = "New Value";
int index = lstMarks.IndexOf(item);
lstMarks[index] = yourObjectToUpdate;
}
You are extracting an obj from marketWeAreInterestedIn list using LINQ's firstOrDefault extension. This is a new object and not a reference to the obj in that list. Therefore, no updates will be reflected in the object inside that list. Try using 'indexof'
You are not storing "list of interfaces" in your list. List<T> stores an array of pointers to objects that support T interface. Once you enumerate (with Linq in your case) your list, you copy a pointer from list, which is not associated with list itself in any way. It is just a pointer to your instance.
To do what you want, you will have to build new list while enumerating the original one, adding objects to it, according to your needs, so the second list will be based on the first one but with changes applied that you need.
You can also replace specific instance at specific index instead of building new list in your code, but to do this you will need to enumerate your list with for loop and know an index for each item:
list[index] = newvalue;
But there is a third solution to update list item directly by Proxying them. This is an example
class ItemProxy : T { public T Value { get; set; } }
var list = new List<ItemProxy<MyClass>>();
list.Insert(new ItemProxy { Value = new MyClass() });
list.Insert(new ItemProxy { Value = new MyClass() });
list.Insert(new ItemProxy { Value = new MyClass() });
foreach(var item in list)
if(item // ...)
item.Value = new MyClass(); // done, pointer in the list is updated.
Third is the best case for perfomance, but it will be better to use this proxying class for something more than just proxying.
I have a array:-
private string[][] barValues = new string[][] { new string[]{ "1.9", "5.8", "4.8", "Since Inception", "24-Jan 2014 to 24 Jun 2014" },
new string[]{"1.2", "16.5","9.8", "Year to date","01-Apr 2014 to 24-Jun 2014" },
new string[]{"11.6","28.8","23.5","Last quarter","01-Jan to 24-Jun 2014"} };
I want to convert this array into my custom list :-
List<Portfolio> list = new List<Portfolio>();
I tried doing :-
List<Portfolio> list=myArray.Cast<Portfolio>().ToList();
But I get a error:-
System.InvalidCastException: Cannot cast from source type to
destination type.
How do I do this conversion?
You will need to use the Select operator and assign your array of strings to your Portfolio object. Something like this:
myArray.Select(array => new Portfolio { Field1 = array[0], Field2 = array[1] }).ToList()
There is no "magic" conversion from string[] to your class PortFolio, you have to do it manually.
This could work:
List<Portfolio> portFolioList = barValues
.Select(sArr => new Portfolio
{
Values = sArr.Take(3).Select(double.Parse).ToList(),
Name = sArr.Skip(3).First(),
TimeSpan = sArr.Last()
}).ToList();
If you have a class like this:
public class Portfolio
{
public List<double> Values { get; set; }
public string Name { get; set; }
public string TimeSpan { get; set; }
}
Convert Array to string try this way
string[] arr = ...
List<object> list= new List<object>(arr);
foreach(object obj in arr)
list.add(obj);
var converted = barValues.Select(a => new {Value1 = a[0], Value2= a[1]}).ToArray();
This gives you an array of anonymous objects. Just replace my anonymous constructor with your constructor of Portfolio. Lambda variable a is the array that contains the string values.
Create a constructor taking the items from barValues and iterator over those.
like:
class PortFolio()
{
PortFolio(string e1, string e2, string e3, string period, string date)
{
// Copy to properties.
}
...
}
Then in the code for copying:
foreach (var barValue in barValues)
{
list.Add(new PortFolio(barValue[0], barValue[1], barValue[2], barValue[3], barValue[4]); }
}
C# is a strongly typed language. There is no out of the box way to magically cast a bunch of string into an object.
A correct way to do what you want is to first add a constructor on your Portfolio class that takes an array of strings and assign its values to your instance properties:
class Portfolio
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
public string Method { get; set; }
public string Period { get; set; }
public Portfolio(string[] values)
{
if (values != null)
{
this.Value1 = values.ElementAtOrDefault(0);
this.Value2 = values.ElementAtOrDefault(1);
this.Value3 = values.ElementAtOrDefault(2);
this.Method = values.ElementAtOrDefault(3);
this.Period = values.ElementAtOrDefault(4);
}
}
}
You can then use linq to build your list :
var portfoliosList = barValues.Select(values => new Portfolio(values)).ToList();
If required, you can do additional work in the constructor, for instance converting the values to decimal, the method to an Enum and the period as a couple of DateTime.
If you cannot modify the Portfolio class, because it's a third party component or out of your scope, you can create a factory class with a method containing that logic :
static class ProtfolioFactory
{
static public Portfolio BuildPortfolio(string[] values)
{
var portfolio = new Portfolio();
if (values != null)
{
portfolio.Value1 = values.ElementAtOrDefault(0);
portfolio.Value2 = values.ElementAtOrDefault(1);
portfolio.Value3 = values.ElementAtOrDefault(2);
portfolio.Method = values.ElementAtOrDefault(3);
portfolio.Period = values.ElementAtOrDefault(4);
}
return portfolio;
}
}
The linq query then becomes :
var list = barValues.Select(values => ProtfolioFactory.BuildPortfolio(values)).ToList();
I am new to c#, but not to programming. First of, thank you so much for your help!
I want a class or struct whichever is appropriate which has 3 variables. A string, and two datetimes.
I want to create a loop which stores new classes in a list.
Something like:
for each item in dataViewer
create new class
assign variables
store class in list
next
Thank you so much for your help
You can do this easily with LINQ:
var list = dataViewer
.Select(item => new YourClass
{
StringProperty = ...,
DateTimeProperty1 = ...,
DateTimeProperty2 = ...
})
.ToList();
It lets you state your intentions (create a list of YourClass objects from each item in dataViewer) without emphasizing the mechanics behind it (loops, etc.)
Edit: If you don't require a list, just a sequence, this also looks nice using the query syntax (same meaning):
var yourClasses =
from item in dataViewer
select new YourClass
{
StringProperty = ...,
DateTimeProperty1 = ...,
DateTimeProperty2 = ...
};
Maybe something like this
var list = new List<YourClass>();
foreach(var item in dataViewer) {
var cls = new YourClass();
// Assign variables here
// cls.Test = item.Test;
list.Add(cls);
}
Try this:
public class YourClass
{
public string YourString {get; set;}
public DateTime YourDate1 {get; set;}
public DateTime YourDate2 {get; set;}
public YourClass(string s, DateTime d1, DateTime d2)
{
YourString = s;
YourDate1 = d1;
YourDate2 = d2;
}
}
public List<YourClass> Read()
{
List<YourClass> list = new List<YourClass>();
foreach(var item in dataViewer)
list.Add(new YourClass(s,d1,d2)); // Read variables from item...
return list;
}
public class Appropriate
{
public string Value { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
IList<Appropriate> list = new List<Appropriate>();
foreach(var item in dataViewer) {
list.Add(new Appropriate() {
Value = item["value"],
Start = item["start"],
End = item["end"]
});
}
IList<Appropriate> list = new List<Appropriate>();
dataViewer.ToList().ForEach(i => list.Add(new Appropriate() {
Value = item["value"],
Start = item["start"],
End = item["end"]
});
public class Foo
{
public Foo(string name, DateTime dt1, DateTime dt2)
{
Name = name;
DT1 = dt1;
DT2 = dt2;
}
public string Name { get; set; }
public DateTime DT1 { get; set; }
public DateTime DT2 { get; set; }
}
public class Example
{
public List<Foo> example(DataView dataViewer)
{
var foos = new List<Foo>();
foreach(var data in dataViewer)
{
foos.Add(new Foo(data.Name, data.DT1, data.DT2);
}
return foos;
}
}