C# Tuple of List replace/erase previous data - c#

Thank you for all your messages that really helped me, I finally decide to post here. I just discovered Tuples which allow me to create List of List with other element.
Here is my simplified code :
public class Category { //Classe "Category" comprenant le nom de la category et sa proportion de CIR
public string category_name {get; set;}
public string proportion {get; set;}
}
public static void Main(string[] args)
{
var Projects = new List<Tuple<string, List<Category>>>();
while(...)
{ [...]
var List_Categories = new List<Category>()
while(...)
{ [...]
List_Categories.Add(category_example);
}
Projects.Add(Tuple.Create(nom_projet, List_Categories);
}
}
My "Category" class is just 2 strings. A "project" is a List AND a string (project_name) hence my using of Tuple for this.
1st question : I would have create a class for "project" but I could not find how to put a Tuple as a parameter of a class ? (he do not recognize the "var" type as a parameter)
Then, 2nd question : I have an issue : when the program going on, inside "Projects" (which is a List of "project"), the "project_name" is writting well but the "Category" data is each time replaced by the new one for EVERY index of the list. I do not know how such a thing is possible ...
Thank you very much, I hope you can understand my problem, I am very sorry about my English but it is not my native language. I will reformulate if it's not understandable.

It doesn't seem like you want a tuple as a property in your project class, it seems like what you want is this:
public class Project
{
public string project_name {get; set;}
public List<Category> categories {get; set;}
}
And as Corak mentioned, I think a Dictionary might help with your other issue.
By the way, the reason you can't use var as the type of a property is that it isn't a type -- it's just a shorthand way of declaring a variable without explicitly specifying the type of that variable (the compiler figures it out), but it only works on local variables. You can't use it for a property because the compiler would have no way to figure out what the type of that property is supposed to be. The type of a tuple is Tuple<T1, T2,...> -- for example, your tuples here are Tuple<string, List<Category>>, and you could certainly have a class property of that type if you wanted.

Try using a dictionary instead
public static void Main(string[] args)
{
var Projects = new Dictionary<string, List<Category>>();
while(...)
{ [...]
var List_Categories = new List<Category>()
while(...)
{ [...]
List_Categories.Add(category_example);
}
Projects.Add(nom_projet, List_Categories);
}
}
By creating a tuple on every single loop, you are overwriting previous work. With a dictionary you can simply add to your collection inside of your loops

Related

How to get access to an object which is in a different class while maintaining encapsulation? (data?)

I'm making a Phone Book console application in c# and It's my biggest project so far :)
I consider myself as a beginner but I would love to improve
I am using 3 classes to create this, here they are:
Modifications
Search
Display
Modifications class is responsible for adding, deleting and editing contacts. Search and Display classes are self explanatory. I also have interfaces for each of these classes.
Problem:
I created a List of KeyValuePair inside Modifications class, like this:
private List<KeyValuePair<string , int>> AllContacts { get; set; }
public Modifications()
{
AllContacts = new List<KeyValuePair<string, int>>();
}
I would like to access this AllContacts object in my other classes. But I failed to achieve this while maintaining encapsulation. AllContacts is like my main source of data. I add contacts in this List. I don't want someone in my main method to be like modificationObject.AllContacts.Clear();
My Attempts:
[ skip this if you prefer :) ]
My Sad Attempt Part 1:
-Inherited Display Class from base class Modifications
-in my Modifications class, I created a protected property of type List<KeyValuePair<string , int>> and named it 'AllContacts' as a database
-in my Display class , I created a private property of same type as above and named it _allContacts
-inside Constructor of my Display class, I requested a Modification type object named it modObject
-I stated , _data = modObject.AllContacts;
-It didnt work, I presume it's because only derived objects are able to get this property?
My Sad Attempt Part 2:
-Assuming my assumption is true, I tried to downcast the modObject to its derived class type, Display. Got compilation error instead. Tried to make a protected GetAllContacts() method, compilation error.
My Sad Attempt Part 6:
-In end, I just made it a public property with a private setter. My application works, But people could still go x.AllContacts.Clear() in my main method somehow.
Questions:
How can I use AllContactsobject in other classes without breaking encapsulation? Is it even possible?
Initially, I created a separate class for data and I had 3 other classes (Modification, Search and Display) , its a better design, right? I had same problem in accessing that data object in my other classes.
Thanks :)
Additional Context if required: (currently working application , no methods)
Modification class:
public class Modifications
{
public List<KeyValuePair<string, int>> AllContacts { get; private set; }
public Modifications()
{
AllContacts = new List<KeyValuePair<string, int>>();
}
....
Display class:
public class Display : IDisplayable
{
private List<KeyValuePair<string, int>> AllContacts;
public Display(Modifications modificationsObject)
{
AllContacts = modificationsObject.AllContacts;
}
....
You don't need to declare accessors for a private variable member unless you want to disallow the setter for all others methods but it is more speed optimized to use readonly field than property that avoid useless CPU Proc Calls that consume ticks timings for nothing.
To access it from other classes you must declare it as public like that:
public List<KeyValuePair<string , int>> AllContacts { get; private set; }
So others classes can read the reference and invoke variables accessors and methods of the List instance like Count and Add but they can't replace this instance reference and thus change the object itself.
But if you want to disallow manipulating of the list, you may implement all wrapped methods you want to expose like Count, Add, Delete, Remove and so on as well as indexer and you can indicate that the class implements IExumerable<KeyValuePair<string , int>>.
By this way you can have a strong encapsulation:
public class MyClass: IEnumerable<KeyValuePair<string , int>>
{
private readonly List<KeyValuePair<string , int>> AllContacts
= new List<KeyValuePair<string , int>>();
public int ContactsCount
{
get { return AllContacts.Count; }
}
public KeyValuePair<string , int> this[int index]
{
get { return AllContacts[index]; }
set { AllContacts[index] = value; } // don't declare if readonly
}
// if adding is allowed
public int Add(KeyValuePair<string , int> item)
{
...
}
...
}

Create overrideable enum in parent class

I want to create a nested structure where every class represents a country, inheriting the same parent class Country. Each child class should have an enum representing the different states States.
The goal is being able to select a country, then one of its states.
The Content will be saved into a dictionary Dictionary<Tuple<string, Type>, object> where the Types would be Country and Country.States.
I tried making an interface/abstract class with an enum called States to be implemented, but this does not work, as it is a type definition.
Is there any workaround?
public abstract class Country
{
public abstract enum States { get; }
}
public class CountryA : Country
{
public new enum States
{
StateA,
StateB,
StateC,
}
}
Your design is flawed, you need to create a single Country class with a property e.g. public string[] States { get; set; }.
Then create instances (objects) of your Country class, each with States set to the items that are needed:
var usa = new Country { Name = "USA", States = new[] { "Alabama", ... } };
var canada = new Country { Name = "Canada", States = new[] { ... } };
// etc
You have a few options:
You can create an enum at runtime (see here: Dynamically create an enum), but I don't think that'll suit your needs, as I imagine you're going down the enum route for ease of use in coding than anything else.
You could implement a typesafe enum pattern (see here: typesafe enum pattern), but that's even more coding just for the ability to use a design that mimics enums while your coding the rest of your logic.
My advice is to use a dictionary and build your 'states' at instantiation from a settings file or external data source. After all, countries and their states/cities/etc do change names from time to time. Locking yourself into a hard-coded situation like what you're aiming for isn't going to support such future changes.
Good luck!
[Edited following response from camilo-terevinto]
While I certainly agree that your design is most likely flawed, since you'd need hundreds of classes and enums, I disagree entirely with the other answers that "it is not possible".
It's certainly possible using generics (while keeping in mind you cannot restrict entirely to Enums):
public abstract class Country<TStates>
where TStates: struct, IConvertible, IFormattable, IComparable
{
public abstract TStates[] States { get; }
}
public enum UnitedStatesStates
{
WhoCares, WhoCares2
}
public class UnitedStatesCountry : Country<UnitedStatesStates>
{
public override UnitedStatesStates[] States { get; }
}
Now, I highly doubt this will be useful in the (not-so-long) term.
You are asking to make enum inheritable, this is possible to achieve if you don't use enum, but a class with static public members (which can be inherited and have different set of members per type). It behave nearly as enum:
public class Country1
{
public static State State1 { get; } = new State("State 1");
public static State State2 { get; } = new State("State 2");
...
}
It should be clear what Country1.State1 is, right? The State can be a more complex object than just a string. It doesn't require inheritance as you can see, because country define states as different members.
You can follow same principle to implement long chain of objects: Planet.Continent.Country.State.Province.Town.Street.Hause..
You say
Content will be saved into a dictionary Dictionary<Tuple<string, Type>, object> where the Types would be Country and Country.States.
Don't. Those are different types, that's a poor choice of a key. If you need to enumerate (to find) states, then just add another member to a Country:
public static IEnumerable<State> States
{
get
{
yield return State1;
yield return State2;
...
}
}
Then the searching for something can be a simple linq:
var stateAInCountry1 = ...Countries.OfType<Contry1>().Single().States.Single(o => o.Name == "A");
var countriesWithStateA = ...Countries.Where(o => o.States.Any(o => o.Name == "A"));
Not sure what problem are you solving by introducing a dictionary, but you can initialize additional data structure with proper key if you provided a way to iterate with easy.
It is not so clear to me, if there is anything else you want to achieve, besides being reminded by the compiler to define these different (!) enums.
Actually they have nothing in common to begin with, so neither the compiler nor you can draw any advantage of that contract.
What you could do is declare it as
public abstract string[] States {get;}
and obtain these strings from the individual enums you define in the derived classes. Then the common thing would probably be that you want the string result for informative purposes or something.

Getting repeated fields via inheritance with GetFields()

I have a problem while working with GetFields() method while trying to get all the fields from a user defined class.
I have a parent class with, lets say two public fields, and another class that inherits from the parent and overrides one of the parent fields with the new keyword.
Problem comes when I use the GetFields method with the second class, because it's returning all the fields, including the overriden one. Is there something that I am doing wrong or a solution for only getting in this case the GoodBye field from the Parent class and the only one Hello field, the one from the inherited class?
This is a simple example of what I'm saying:
public class Foo
{
public string Hello = "sdfsafda";
public string GoodBye = string.Empty;
}
public class Bar : Foo
{
public new string Hello = "jl";
}
static void Main(string[] args)
{
var a = new Bar();
var derp = new List<string>();
foreach (var fieldInfo in a.GetType().GetFields())
{
derp.Add(fieldInfo.Name);
}
Console.WriteLine(derp.Count);
// writes 3 instead of 2
}
About using Properties instead of Fields, I'm afraid I can't use them right now because of limitations that are not in my hand to solve in the project I'm working on.
It seems there is no pre-collected information about shadowed fields available. If you just need a simple workaround, you can first get the names of all fields without duplicates and then use GetField(fieldName) to only get the most derived field instance:
var type = a.GetType();
foreach (var field1 in type.GetFields().Select(x => x.Name).Distinct())
{
var fieldInfo = type.GetField(field1);
derp.Add(fieldInfo.Name);
Console.WriteLine(fieldInfo.DeclaringType);
Console.WriteLine(fieldInfo);
}
ofcourse, if you only need the field names like in your question code, instead of the FieldInfo object, you can stop after the .Select(x => x.Name).Distinct().

Copy doubles and integers from one list to another using LINQ

I have an object of type scenario that contains a list of objects type SN. There is another object of type scenarioSchema that also contains a list of objects type SNSchema.
I want to copy items (Doubles and Integers) from type SNSchema into type SN of object scenario.
scenario.V = new List<SN>();
scenario.V.Add(scenarioSchema.V.Select(x => x.Distro);
public class SN
{
public double Version { get; set; }
public int Distro { get; set; }
}
I know this behavior can be achieved through LINQ, basically what I want to do is to go through the whole list of items SNSchema and pass those to the list of object scenario.
I know this might be too simple, but I can't wrap my head around this.
Thanks,
I think you need AddRange()
scenario.V.AddRange(scenarioSchema.V.Select(x => new SN() { Distro = x.Distro}));

How can i generate missing values using table.CreateSet<>(myClass)() in SpecFlow?

I'm using SpecFlow for the very first time to write tests for my project and I ran into a small problem.
I have the next class:
public class FancyName
{
[DataMember]
public Guid Guid { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public List <Country> Countries { get; set; }
}
And I want to generate this class in my Tests using SpecFlow helpers.
Here is the part of Scenario:
[...]
When i add some names
| Name | Countries |
| UK | 1 |
| US | 2 |
[...]
I try to parse it in step definitions like this:
[When(#"I add some names")]
public void AddNames(Table table)
{
var names = table.CreateSet<FancyName>();
[...]
}
And I'm running into 2 problems:
I do not pass the Guid because a want to generate it like Guid.NewGuid() so created object contain null
I pass countries as sorting but i need to create List<Country>().
I used to try iterate through Table and create FancyName objects manually but as I understand it is not SpecFlow way. I tried to look through documentation and wasn't lucky to find proper solution.
May be somebody know the really good way to solve this?
Thanks in advance.
The main SpecFlow.Assist author here...
I agree with #sam-holder above that Table.CreateSet<T> is not magic, and perhaps the transform is an easier solution in this case. But Assist does, in deed, have the features needed to do what is asked. :)
I'd like to explain how. I see two issues presented as:
1) How to set the Guid on each record?
2) How to convert the string value to an array?
For (1), the answer is pretty simple. You can pass a function to CreateSet that tells the library how to instantiate the object you want to create. Since you just want to set the Guid, you can do it like so:
table.CreateSet<FancyName>(() => new FancyName { Guid = Guid.NewGuid()});
// or simpler, table.CreateSet(() => new FancyName { Guid = Guid.NewGuid()});
For (2), you'll have to do a little more programming. You want Assist to know how to convert the string to a list. To do this, you'll have to create a "value retriever" and register it with Assist. I was able to do this with the following code:
public class CountryRetriever : IValueRetriever
{
public bool CanRetrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
{
return propertyType.FullName.StartsWith("System.Collections.Generic.List`1[[SpecFlowExample.Country");
}
public object Retrieve(KeyValuePair<string, string> keyValuePair, Type targetType, Type propertyType)
{
return keyValuePair.Value.Split(',')
.Select(x => new Country {Name = x})
.ToList();
}
}
[Binding]
public class Steps
{
[BeforeTestRun]
public static void Setup()
{
TechTalk.SpecFlow.Assist.Service.Instance.RegisterValueRetriever(new CountryRetriever());
}
[When(#"i add some names")]
public void WhenIAddSomeNames(Table table)
{
var things = table.CreateSet<FancyName>(() => new FancyName { Guid = Guid.NewGuid()});
}
}
Pointing directly at a list like this, especially using a string, is pretty hacky, but this code does work.
The country retriever will announce that it can handle the transformation of a string when it is confronted with a List<Country> type. It then splits the string and creates a list of countries.
SpecFlow starts each test run with value retrievers for most of the base .Net types, but not your List<Country>. To accomodate your type, you'll need to register your new value retriever before every test run.
Table.CreateSet<> can't perform magic. It can't know that its supposed to create a new Guid for your object, or that its supposed to create a list containing 2 countries. You'll have to create this object yourself I think.
The best way to solve this is to use a [StepArgumentTransformation]
something like this:
[StepArgumentTransformation]
public List<FancyName> TransformToFancyName(Table table)
{
//create the list from the table contents
}
[When(#"I add some names")]
public void AddNames(List<FancyName> names)
{
.. use your FancyNames here
}
specflow will call your StepArgumentTransformation for any Step which has an argument of List<FancyName> as the last parameter and a corresponding table in the feature
You can think about something like Nested Tables?, but according to this post it's a bad idea. It suggests to introduce additional step to populate complex objects.

Categories