Creating objects from a list which can vary in length - c#

I'm a beginner and trying to create a small app, which finds the best combination of license plans given customer requirements.
I've simplified the part I am having trouble with below. The plangroup is actually a class of other objects, so it is basically used to group different plans together (e.g. where they are compatible). So it's a nested class, but I haven't included that part for simplicity.
My issues are:
I need to create planGroup objects based on the info in a list. The length of the list can vary.
So the I have mutiple constructors so that objects can be created with different number of fields, is there a more concise way of doing that then writing a constructor for each possible number of arguments?
How can I create the object from the list, without using multiple if statements to work out the length of the list?
Once created, how can I get the values out of the object, given that I don't know how long it is, again without using multiple if statements?
Thanks and hope this makes sense!
using System;
using System.Collections.Generic;
using System.IO;
namespace ConsoleApp1
{
class Program1
{
static void Main(string[] args)
{
// easy to create an object when you know how many bits of info you have
PlanGroup p1 = new PlanGroup("Plan 1 name", "Plan 2 name", 350);
Console.WriteLine($"{p1.Plan1} / {p1.Plan2} / {p1.Cost}");
// But what if we have a list of plan info that comes from another method, and could vary in length
var planinfo = new List<string> { "Plan name", "Another plan name" };
// could create the group object like this, but planinfo length will vary. The cost will be calculated and is not an issue.
// but I don't want to do this as would have to use multiple if statements to based on planinfo count (in reality there might be up to 10).
PlanGroup p2 = new PlanGroup(planinfo[0], planinfo[1], 200);
// then also need a way of getting the details of the PlanGroup objects that is better than this, so will return all the plans as well as the cost
Console.WriteLine($"{p2.Plan1} / {p2.Plan2} / {p2.Cost}");
}
class PlanGroup
{
public string Plan1 { get; set; }
public string Plan2 { get; set; }
public string Plan3 { get; set; }
public string Plan4 { get; set; }
public int Cost { get; set; }
public PlanGroup(string Plan1, string Plan2, int Cost)
{
this.Plan1 = Plan1;
this.Plan2 = Plan2;
this.Cost = Cost;
}
public PlanGroup(string Plan1, string Plan2, string Plan3, int Cost)
{
this.Plan1 = Plan1;
this.Plan2 = Plan2;
this.Plan3 = Plan3;
this.Cost = Cost;
}
public PlanGroup(string Plan1, string Plan2, string Plan3, string Plan4, int Pages)
{
this.Plan1 = Plan1;
this.Plan2 = Plan2;
this.Plan3 = Plan3;
this.Plan4 = Plan4;
this.Cost = Cost;
}
}
}
}

For your second issue. You could use System.Collections.Generic to use a List<T>. In which you could just have a List<string> Plans in the class and have a similar constructor. I.E the constructor takes a List<string> as a parameter
using System.Collections.Generic;
public class PlanGroup {
public List<string> Plans { get; set; }
public int Cost { get; set; }
public PlanGruop(int Cost, List<string> Plans) {
this.Cost = Cost;
this.Plans = Plans;
}
}
and the utilization of the constructor would look like
PlanGroup p = new PlanGroup(500, new List<string>{ "plan1", "plan2"... });
then to get the length (number of "plans" inside the list) You simply use p.Plans.Count. And accessing an element, is as easy as looking through an array p.Plans[0] and so on.
The question was a bit unclear, but i hope this gave you some answers.

Related

c# how to increment id for each instance of class automatically

A user inserts KID NAME, the code instantiate an object and add it to list of objects.
How to give each object a unique number automatically starting at 1, such that first KID(object) gets number 1, second KID gets number 2 etc.
I tried something like this but the result is "stackoverflow".
How would the class should be?
class Kid
{
public string KidName{ get; set; }
private static int Number { get; set; }
public int KidNumber { get; set; }
public Kid (string name)
{
this.KidName = name;
Number++;
this.KidNumber = Number;
}
}
Your code works successfully on my device.
Maybe you have an 'stackoverflow' for other reasons?
If you need only to identify Kid class, the alternative way is to use Guid:
class Kid
{
public string KidName { get; set; }
public Guid KidNumber { get; set; }
public Kid(string name)
{
this.KidName = name;
this.KidNumber = Guid.NewGuid();
}
}
Nothing in your example can induce a StackOverflowException. Stackoverflows occur when you repeatedly call the same method recursively either directly or indirectly (by way of an intermediate) or when you have an instance-level field of the same type as the instance itself (a special case of the former). You don't exhibit either case in your example so it must be due to something else external to the code you've shown. The most your code could potentially exhibit is an OverflowException if you were in a checked context.
That said, your approach is OK at first glance with the obvious pitfall that it is not thread-safe. Two instance could in theory end up with the same KidNumber. You can fix that by making use of the Interlocked class:
using System.Threading;
class Kid
{
private static int _counter;
public string KidName { get; set; }
public int KidNumber { get; set; }
public Kid(string name)
{
this.KidName = name;
this.KidNumber = Interlocked.Increment(ref _counter);
}
}
Like ++ in an unchecked context, this handles the case of overflow; once you hit int.MaxValue, if will automatically wrap to int.MinValue. If you don't want negative IDs you'll have to handle that seperately.

Set array values on class runtime (and set Attributes onto them)?

I was unable to define what to name this problem, thus tried google and here, but cant find.
Is there any way to set array(or dictionary) members value on property level, and set attribute onto them? like this:
public class XYZ{
private string[] x= new string[]{"smth", "smth"};
x[0] { get; set; }
[Receipt(order=2, name="warranty")]
x[1] { get; set; }
....
}
So, mainly I have to questions to be frank:
1) Is that possible to set attributes onto array members?
2) Is that possible (forget attribute) to assign value to array member in property level (not within method), like this?:
public class XYZ{
private string[] x= new string[2]();
x[0] = "smth";
}
The answer is definitely no. Attributes can only be added onto compile-time features such as classes and methods. Not onto runtime features such as object data.
Also your getter/setter syntax is very wrong, it can only be like this:
public int Month { get; set; }
So an access modifier (public; optional), followed by a Type (int), followed by the name (Month), and then the get and/or set specification.
You can't do it exactly the way you want, but you can get close:
public class XYZ
{
private string[] x= new string[]{"smth", "smth"};
[Receipt(order=1, name="warranty")]
public string Receipt1 {get { return x[0];} set{x[0] = value;} }
[Receipt(order=2, name="warranty")]
public string Receipt2 {get { return x[1];} set{x[1] = value;} }
//...
}
Of course, the number of entries here are fixed, and you have to give these properties a real, meaningful name. You can't have a dynamic number of entries that changes while the program runs.
The one other thing you can look at is an indexer property. With an indexer, you can only set your attribute once on the whole property, rather than individual elements, but it will allow the number of items to change at run time.
The short answer is no. Attributes are for metadata, and this looks like actual data. Probably you should have your array be an array of some class that contains the value (i.e. smth) and all the data you wanted to have in your ReceiptAttribute.
So something like:
public class Receipt
{
public string Value {get; set;}
public int Order {get; set;}
public string Name {get; set;}
}
And then:
public class XYZ
{
private Receipt[] x= new Receipt[]
{
new Receipt() { Value = "smth", Order = 1, Name = "warranty" },
new Receipt() { Value = "smth", Order = 2, Name = "warranty" },
};
}
And, obviously, when you want the value you'd do something like:
x[0].Value

protobuf-net: Handling nulls in IEnumerable? Via what attributes/settings?

Some sample code below. The interesting/problem case is the Data property in
Mad. This code blows up (null value in the enumerable). Also, it works if i don't use the static attributes but instead the runtime type model, where i put in member.SupportNull = true for the fields (which is the behaviour i want), so what am i missing in the attributes / settings? Google search seems to indicate this is an open issue with probuf-net? That the same functionality is not available via attributes?
As as aside, if someone could suggest a way - i really love the runtime type model, i want to use that everywhere with a nice compiled model... but with it i lose the object versioning that protocol buffers solves! (via explicit tags). Is there any good way to maintain object version compatibility (simply adding fields) without doing all the static notation with fixed tags?
Basically the key thing with the runtime model is the assignment of tag indices and i can't think of a way of handling versions without explicitly specifying the tag indices via attributes...
[ProtoContract]
[ProtoInclude(1, typeof(ing))]
public class Eff
{
[ProtoMember(2)]
public string gg { get; set; }
}
[ProtoContract]
public class ing : Eff
{
[ProtoMember(1)]
public int zz { get; set; }
}
[ProtoContract]
public class Mad
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public IEnumerable<ing> Data { get; set; }
[ProtoMember(3)]
public ing Single { get; set; }
}
private static void Main(string[] args)
{
var obj = new Mad
{
Name = "test"
,Data = new[] { new ing {gg = "ooga", zz = -101},null,new ing()}
,Single = new ing {gg = "abc", zz = -999}
};
var m = new MemoryStream();
Serializer.Serialize(m, obj);
m.Seek(0, SeekOrigin.Begin);
var copy = Serializer.Deserialize<Mad>(m);
}
Short answer, it seems unavailable via attributes.
Workaround i'm doing for now - for every single type of interest(including the whole inheritance hierarchy) - add it to the type model yourself (with default handling so that it processes attributes), then call .GetFields() and set .SupportNull = true for each field (or only the relevant one)

How do I map List<T1> to List<T2> using LINQ (or otherwise)

Lately I've worked on some programs that involve translating objects across various data domains. So I have a lot of mapping methods (sometimes as extension methods) for translating one type of object to another similar type in a different domain. Often, I also need a way to translate a List<> to a List<> of said types. This always involves having a method that simply creates a List<> of the target type, runs a foreach loop to add every element of the source List<> (but using the mapping method on each) and returning the new list. It's feeling pretty repetitive and like there might be something built into the language to do this (perhaps in LINQ?). I've looked at several similar issues involving List.ForEach() and the pros and cons of it (not what I'm looking for anyway). I'll illustrate with some example code below. Maybe there is no way to do what I want, and if that's the answer, then that's the answer, but I hope maybe there is. Please note, this is obviously just example code and comments about my overall program design won't really add anything because this is a very small dummy version of the problem at hand.
class A
{
public Guid Id { get; set; }
public string Email { get; set; }
public string MemberCode { get; set; }
}
class B
{
public string Email { get; set; }
public string MemberCode { get; set; }
// My custom mapping method
public A MapToA()
{
return new A()
{
Id = Guid.NewGuid(),
Email = this.Email,
MemberCode = this.MemberCode
};
}
// For list mapping, I have this, but I'd prefer
// to do something else that could utilize my custom mapper.
// Perhaps a built in LINQ method?
public static List<A> MapToListOfA(List<B> listOfB)
{
List<A> listOfA = new List<A>();
foreach (var b in listOfB)
{
listOfA.Add(b.MapToA());
}
return listOfA;
}
}
// Class C shows what I currently do that I'd like to get
// away from:
class C
{
public List<A> ListOfA { get; set; }
// other properties unrelated to the problem
// This is how I might use the MapToListOfA method,
// but I'd rather have something better.
public C(List<B> listOfB)
{
this.ListOfA = B.MapToListOfA(listOfB);
}
}
// I'd like something more like this:
class D
{
public List<A> ListOfA { get; set; }
// other properties unrelated to the problem
public D(List<B> listOfB)
{
// This doesn't compile, of course, but I hope
// it illustrates what I'm intending to do:
this.ListOfA = listOfB.Select(b => b.MapToA());
}
}
// This doesn't compile, of course, but I hope
// it illustrates what I'm intending to do:
this.ListOfA = listOfB.Select(b => b.MapToA());
It doesn't compile because listOfB.Select(b => b.MapToA()) produces an instance of IEnumerable<A> which is not assignable to List<A>.
Use ToList and it should compile fine
this.ListOfA = listOfB.Select(b => b.MapToA()).ToList();

C#: Adding to a List of Type SomeClass

I have this class:
public class Lockbox
{
string lockbox { get; set; }
int polling_interval { get; set; }
}
In another class I made a List of Type Lockbox:
var monitor_lockboxes = new List<Lockbox>();
Now how do I add entries to the list? Can I do this?:
monitor_lockboxes.Add(...);
But it does not take 2 arguments.
But it does not take 2 arguments.
Well no, it wouldn't. It takes one argument, of type Lockbox.
It sounds like you want something like:
var newBox = new Lockbox { lockbox = "foo", polling_interval = 10 };
monitor_lockboxes.Add(newBox);
You can do it in a single statement, of course - I've only separated it out here for clarity.
(I'd also strongly advise you to change your naming to follow .NET conventions.)
The following would work:
monitor_lockboxes.Add(new Lockbox { lockbox = "Foo", polling_interval = 42 } );
This uses the the Object Initializer syntax. For this to work, the properties on Lockbox have to be public.
Another approach would be to provide a constructor to Lockbox that takes the values of the properties:
public class Lockbox
{
public Lockbox(string lockbox, int pollingInterval)
{
this.lockbox = lockbox;
this.polling_interval = pollingInterval;
}
public string lockbox { get; set; }
public int polling_interval { get; set; }
}
Now you can use it like this:
monitor_lockboxes.Add(new Lockbox("Foo", 42));

Categories

Resources