Concatenate properties with index in c# - c#

I have a class Student with properties sub1,sub2, and sub3.
I want to access those properties using a loop by concatenating the property name
with an index in order to avoid duplication. I tried below code
public class SampleApplication
{
public static void Main(string[] args)
{
Student s =new Student();
for(int i=1;i<=3;i++)
{
s.$"sub{i}"="Subjects";
}
}
}
public class Student
{
public string sub1;
public string sub2;
public string sub3;
}
But I am getting an error like the identifier expected.
Can anyone help me to solve this? Thanks in advance.

You need either use reflection or define an indexer:
public class Student
{
public string sub1;
public string sub2;
public string sub3;
public string this[int index]
{
get => index switch
{
1 => sub1,
2 => sub2,
3 => sub3,
_ => throw new ArgumentOutOfRangeException()
};
set
{
switch (index)
{
case 1:
sub1 = value;
break;
case 2:
sub2 = value;
break;
case 3:
sub3 = value;
break;
default: throw new ArgumentOutOfRangeException();
}
}
}
}
And usage:
Student s = new Student();
for (int i = 1; i <= 3; i++)
{
s[i] = "Subjects";
}

Related

sorting a generic list from within a generic container class

I need to sort a list by any one of its properties, but i dont know which of these properties it will specifically be sorted on. The Main method below.
static void Main(string[] args)
{
Things<Something> something = new Things<Something>();
something.Add(new Something
{ Thing = "Apartment", Price = 1500000 });
something.Add(new Something
{ Thing = "Bed", Price = 10000 });
something.Add(new Something
{ Thing = "Lamp", Price = 600 });
something.Add(new Something
{ Thing = "Car", Price = 5000000 });
Console.WriteLine("\n\tStuff sorted by description");
something = something.SelectionSort("Thing");
foreach (Something thing in something)
Console.WriteLine("\t" + thing);
Console.WriteLine("\n\tStock items sorted by value");
something = something.SelectionSort("Value");
foreach (Something thing in something)
Console.WriteLine("\t" + thing);
Console.Write("\n\tPress any key to exit ...");
Console.ReadKey();
}
I have a struct
public struct Something
{
public string Thing { get; set; }
public decimal Price { get; set; }
}
And a generic container class called things
public class Things<T> : IEnumerable<T>
{
private List<T> lstItems;
public int Count { get { return lstItems.Count; } }
public Things() { lstItems = new List<T>(); }
public Things(List<T> items_) { lstItems = new List<T>(items_); }
public void Add(T item)
{
lstItems.Add(item);
}
public T this[int i]
{
get { return lstItems[i]; }
set { lstItems[i] = value; }
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new System.NotImplementedException();
}
public IEnumerator<T> GetEnumerator()
{
foreach (T item in lstItems)
yield return item;
}
}
An extensions class extends the generic container class
public static class ExtensionsClass
{
private static string SortFiield { get; set; }
private static object GetProperty<T>(T thing, string nameofProp)
{
return thing.GetType().GetProperty(nameofProp).GetValue(thing, null);
}
private static int Compare<T>(T x, T y)
{
IComparable propX = (IComparable)GetProperty(x, SortFiield);
IComparable propY = (IComparable)GetProperty(y, SortFiield);
return propX.CompareTo(propY);
}
public static Things<T> SelectionSort<T>(this Things<T> things, string SORTFIELD)
{
List<T> lsstt = new List<T>(things);
int iIndex;
T temporary;
SortFiield = SORTFIELD;
for (int i = 0; i < lsstt.Count - 1; i++)
{
iIndex = i;
for (int j = i + 1; j < lsstt.Count; j++)
{
string first = GetProperty(lsstt[j], SortFiield).ToString();
string second = GetProperty(lsstt[iIndex], SortFiield).ToString();
if (Compare(first, second) < 0)
iIndex = j;
}
temporary = lsstt[i];
lsstt[i] = lsstt[iIndex];
lsstt[iIndex] = temporary;
}
return new Things<T>(lsstt);
}
}
The problem i am encountering is that get property in the extension class returns null, but i know that the object i am trying to return exists. It is found by the "String first = ...." line but when getproperty is called from the Compare method then it returns null.
You are passing "first", "second" to Compare. In your case both of them are strings and not objects, you need to pass "lsstt[j]" and "lsstt[iIndex]" to it.
if (Compare(lsstt[j], lsstt[iIndex]) < 0)
iIndex = j;

Create a Variable which refers to the Parameter of another Object?

I have a pretty specific problem.
I have a custom class with various properties of the same type and I have a method elsewhere which needs to run on the class and compare those properties. The method needs to be able to compare these properties, but is instructed to compare different ones depending on the situation.
As it stands, I have a switch which takes a string to determine which properties to compare:
switch(field)
{
case "int1":
if (myClass1.int1 < myClass2.int1)
{
//do something
}
break;
case "int2":
if (myClass1.int2 < myClass2.int2)
{
//do something
}
break;
}
Is there a way that I could just set a variable to refer to the property of the class which I want to refer which would allow just using the switch and have the code comparing the properties later? Something like this:
var referrer;
switch(field)
{
case "int1":
referrer = int1;
break;
case "int2":
referrer = int2;
break;
}
if (myClass1.referrer < myClass2.referrer)
{ //do something }
You could do this using a Func<T, int> approach, something like:
public class SomeClass
{
public int First { get; set; }
public int Second { get; set; }
}
var one = new SomeClass { First = 1, Second = 5 };
var two = new SomeClass { First = 5, Second = 1 };
Func<SomeClass, int> referrer = null;
switch (field)
{
case "first":
referrer = x => x.First;
break;
case "second":
referrer = x => x.Second;
break;
}
if (referrer(one) < referrer(two))
{
}
Of course, this assumes that you always want to compare int properties. Take a look here.
There is another way, if you want to avoid the switch completely, and is to use Reflection:
public class Program
{
public static void Main()
{
var one = new SomeClass { First = 1, Second = 5 };
var two = new SomeClass { First = 5, Second = 1 };
string field = "First";
if (GetValue(one, field) < GetValue(two, field))
{
Console.WriteLine("one is smaller than two");
}
else
{
Console.WriteLine("one is greater than two");
}
}
private static int GetValue(SomeClass someClass, string field) => Convert.ToInt32(typeof(SomeClass).GetProperty(field).GetValue(someClass));
}
public class SomeClass
{
public int First { get; set; }
public int Second { get; set; }
}

C# Assigning to strings from a public class and list

so far my code does the following.
Ask user for a numeric amount for 'players'
Then asks for names for each of the players which is added to a list and class
I'd like to call those names from the list or class (not really sure how class works) and assign it to a new string. Here's what I got so far:
public class NameVariable
{
public int ID { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
bool IsUserWrong = false;
Console.WriteLine("Write amount of players");
while (!IsUserWrong)
{
int TotalPlayers;
while (!Int32.TryParse(Console.ReadLine(), out TotalPlayers))
{
Console.WriteLine("Value must be numeric.");
}
if (TotalPlayers >= 12 && TotalPlayers <= 16)
{
List<NameVariable> PlayerList = new List<NameVariable>();
for (int index = 0; index < TotalPlayers; index++)
{
Console.WriteLine("Enter player {0}'s name:", index + 1);
PlayerList.Add(new NameVariable
{
Name = Console.ReadLine(),
ID = index
});
}
// string player1 = ???
// string player2 = ???
// and so on for 12-16 players
}
else
{
Console.WriteLine("Please enter a value between 12 and 16.");
}
}
}
}
I know that a foreach loop can be used to display all of the variables in the NameVariable class. Would just like to know how to assign each variable to a different string.
Before using the class I just used the list which worked by using
string player1 = PlayerList[0];
string player2 = PlayerList[1];
// and so on for the remaining players
Thanks in advance!
it's just
string player1 = PlayerList[0].Name;
string player2 = PlayerList[1].Name;
...
Essentially your list contains NameVariable objects. PlayerList[index] gives you the object, and .Name gives you the property value of the object.
If you want a specific player name by a specific ID number, you can use LINQ (just to give you a hint)
string player = PlayerList.Where(p => p.ID == WhateverIDNumber).First().Name;
While the answer to your immediate question, i.e., how to access properties of a class object, is as others have shown, I feel like this code has a bigger problem. That is you're trying to do too much in one function, namely, Main(). So I advice to in fact try and refactor your code so that one function does one thing. Something like:
public static int GetNumberOfPlayers()
{
Console.Write("Enter number of players: ");
int totalPlayers;
while (!Int32.TryParse(Console.ReadLine(), out totalPlayers))
{
Console.WriteLine("Value must be numeric.");
}
return totalPlayers;
}
public static List<NameVariable> GetPlayerList(int num)
{
var list = new List<NameVariable>();
for (int i = 0; i < num; i++)
{
Console.WriteLine("Enter player {0}'s name:", i + 1);
list.Add(new NameVariable
{
Name = Console.ReadLine(),
ID = i
});
}
return list;
}
public static void DisplayPlayers(List<NameVariable> list)
{
foreach(var player in list)
{
Console.WriteLine("Player {0}, Name: {1}", player.ID, player.Name);
}
}
public static void CantThinkOfAGoodName()
{
while (true)
{
int totalPlayers = GetNumberOfPlayers();
if (totalPlayers > 16 || totalPlayers < 12)
{
Console.WriteLine("Please enter a value between 12 and 16.");
}
else
{
var playerList = GetPlayerList(totalPlayers);
DisplayPlayers(playerList);
break;
}
}
}
public static void Main()
{
CantThinkOfAGoodName();
Console.ReadLine();
}
Not sure if it helps but you can use an indexer to get players by name.
public NameVariable this[string name]
Let's say you create a class for the colection
public class NameColection : List<NameVariable>
{
public NameVariable this[string name]
{
get
{
return this.FirstOrDefault(n => n.Name == name);
}
}
}
Then you access players by name
var players = new NameColection()
{
new NameVariable() { ID = 1 , Name = "John" },
new NameVariable() { ID = 2 , Name = "Paul" },
new NameVariable() { ID = 3 , Name = "George" },
new NameVariable() { ID = 4 , Name = "Ringo" }
};
var player1 = players["John"];
As NameColection inhertits from List, you will be able to add, remove or modify items the usual way.

Trying to figure out how to access list in a different method

This is my Code inside my Class. I'm trying to figure out how to access Questions list in DisplayQuestion. I have a program.cs that display a menu for a quiz and I can't have anything static.
public string Question { get; set; }
public List<string> Choices { get; set; }
public char[] CorrectChoice = { 'A', 'B', 'C', 'D' };
public List<string> Questions { get; set; }
These are my methods inside my class. I will need to access this list multiple times inside this class.
public void NewQuestion()
{
Questions = new List<string>();
Choices = new List<string>();
Console.WriteLine("Enter the question: ");
Questions.Add(Console.ReadLine());
Console.WriteLine("Enter Choice 1 for the question:");
Choices.Add(Console.ReadLine());
Console.WriteLine("Enter Choice 2 for the question: ");
Choices.Add(Console.ReadLine());
Console.WriteLine("Enter Choice 3 for the question: ");
Choices.Add(Console.ReadLine());
Console.WriteLine("Enter Choice 4 for the question:");
Choices.Add(Console.ReadLine());
// Console.WriteLine("Enter the correct choice(A,B,C,D)");
foreach (string choice in Choices)
{
Console.WriteLine();
Console.WriteLine(choice);
}
}
public void DisplayQuestions()
{
foreach(string question in Questions)
{
Console.WriteLine();
Console.WriteLine(question);
}
}
try declaring and initializing it to null in the global section or create a class with proper getter setter methods for it.
Don't feel constrained to do everything in one class. Even a rough, verbose attempt at separating concerns into separate classes helps with readability, maintainability, and sanity. Then, you're just a thoughtful refactor away from good code.
You are using C#, an OBJECT-oriented language. So go with the grain and embrace using objects.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Quiz_StackOverflow
{
class Program
{
static void Main(string[] args)
{
var quizGenerator = new QuizGenerator();
var quiz = quizGenerator.GenerateQuiz();
var quizProctor = new QuizProctor();
var grade = quizProctor.ProctorQuiz(quiz);
Console.WriteLine(grade.ToString());
Console.WriteLine("Done. Press any key to exit.");
Console.ReadKey();
}
}
public class QuizGenerator
{
public Quiz GenerateQuiz()
{
var problems = GenerateProblems();
var quiz = new Quiz()
{
Problems = problems
};
return quiz;
}
private List<Problem> GenerateProblems()
{
List<Problem> problems = new List<Problem>();
int numChoices = InputValidator.GetPositiveNumber("Enter number of problems: ");
for (int i = 0; i < numChoices; i++)
{
Problem problem = GenerateProblem();
problems.Add(problem);
}
return problems;
}
private Problem GenerateProblem()
{
var question = GenerateQuestion();
var choices = GenerateChoices();
var answer = GenerateAnswer(choices);
var problem = new Problem()
{
Question = question,
Choices = choices,
Answer = answer
};
return problem;
}
private string GenerateQuestion()
{
Console.WriteLine("Enter the question: ");
string question = Console.ReadLine();
return question;
}
private List<string> GenerateChoices()
{
List<string> choices = new List<string>();
int numChoices = InputValidator.GetPositiveNumber("Enter number of choices for the question: ");
for (int i=1; i<=numChoices; i++)
{
string choice = GenerateChoice(i);
choices.Add(choice);
}
return choices;
}
private string GenerateChoice(int index)
{
Console.WriteLine($"Enter Choice {index} for the question: ");
string choice = Console.ReadLine();
return choice;
}
private Answer GenerateAnswer(List<string> choices)
{
Console.WriteLine("Enter the answer: ");
string userChoice = InputValidator.GetUserChoice(new Problem() { Choices=choices });
var answer = new Answer()
{
Value = userChoice
};
return answer;
}
}
public class QuizProctor
{
public Grade ProctorQuiz(Quiz quiz)
{
var answers = new List<Answer>();
foreach(Problem problem in quiz.Problems)
{
Answer answer = ProctorProblem(problem);
answers.Add(answer);
}
Grade grade = quiz.Grade(answers);
return grade;
}
private Answer ProctorProblem(Problem problem)
{
string userChoice = InputValidator.GetUserChoice(problem);
var answer = new Answer()
{
Value = userChoice
};
return answer;
}
}
public class Quiz
{
public List<Problem> Problems { get; set; }
public Grade Grade(List<Answer> answers)
{
List<Answer> answerKey = Problems.Select(x => x.Answer).ToList();
var rawResults = new List<Tuple<Answer, Answer>>();
for(int i=0; i<answers.Count; i++)
{
Answer correct = answerKey[i];
Answer provided = answers[i];
rawResults.Add(new Tuple<Answer, Answer>(correct, provided));
}
return new Grade(rawResults);
}
}
public class Grade
{
private List<Tuple<Answer, Answer>> RawResults { get; set; }
public decimal Percent
{
get { return decimal.Divide(RawResults.Count(x => x.Item1.Equals(x.Item2)), RawResults.Count); }
}
public Grade(List<Tuple<Answer, Answer>> rawResults)
{
RawResults = rawResults;
}
public override string ToString()
{
return string.Format("You scored a {0:P2}.", Percent);
}
}
public class Problem
{
public string Question { get; set; }
public List<string> Choices { get; set; }
public Answer Answer { get; set; }
public string Prompt()
{
Func<int, char> numberToLetter = (int n) =>
{
return (char)('A' - 1 + n);
};
string prompt = Question;
for (int i=0; i<Choices.Count; i++)
{
string choice = Choices[i];
prompt += $"\n{numberToLetter(i+1)}) {choice}";
}
return prompt;
}
}
public class Answer
{
public string Value { get; set; }
public override string ToString()
{
return Value + "";
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
return (obj as Answer).Value.Equals(Value);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}
public static class InputValidator
{
public static int GetPositiveNumber(string prompt)
{
int number = -1;
while (number < 0)
{
Console.Write(prompt);
string input = Console.ReadLine();
try
{
number = int.Parse(input);
}
catch (Exception)
{
Console.WriteLine("ERROR: Please input a positive number.");
}
}
return number;
}
public static string GetUserChoice(Problem problem)
{
Func<char, int> letterToNumber = (char c) =>
{
if (char.IsLower(c))
{
return (int)(c - 'a' + 1);
}
return (int)(c - 'A' + 1);
};
char userChoiceLetter = '_';
while (!char.IsLetter(userChoiceLetter))
{
Console.WriteLine(problem.Prompt());
Console.Write("Answer: ");
var input = Console.ReadLine();
try
{
userChoiceLetter = char.Parse(input);
}
catch (Exception)
{
Console.WriteLine("ERROR: Please input a letter corresponding to your choice.");
}
}
int answerIndex = letterToNumber(userChoiceLetter) - 1;
return problem.Choices[answerIndex];
}
}
}

Declaring a indexer in a class example.

When I run this program in Main() it keep giving me error messages saying that "Tommy", "Bulldog", and "Male" don't exist in current context. I couldn't figure out why? The program was running fine until I add those strings. Can anyone help me understand?
namespace indexusingthismethod
{
public class Dog
{
private string name;
private string breed;
private string gender;
public Dog()
{
name = "Fido";
breed = "Mongrel";
gender = "Male";
}
public Dog(string dogName, string dogBreed, string dogGender)
{
name = dogName;
breed = dogBreed;
gender = dogGender;
}
public string Name
{
get { return name; }
set { name = value; }
}
public string Breed
{
get { return breed; }
set { breed = value; }
}
public string Gender
{
get { return gender; }
set { gender = value; }
}
public string this[int index]
{
set
{
switch (index)
{
case 0: name = value;
break;
case 1: breed = value;
break;
case 2: gender = value;
break;
default: throw new ArgumentOutOfRangeException("index");
}
}
get
{
switch (index)
{
case 0: return name;
case 1: return breed;
case 2: return gender;
default: throw new ArgumentOutOfRangeException("index");
}
}
}
public void Bark()
{
Console.WriteLine("{0} said: Woof!", name);
}
}
public class Doggies
{
static void Main()
{
Dog descriptionofDog = new Dog();
Console.WriteLine("Dog name: {0}\nDog breed: {1}\nDog gender: {2}\n", descriptionofDog [0], descriptionofDog [1],
descriptionofDog [2]);
descriptionofDog[0] = Tommy;
descriptionofDog[1] = Bulldog;
descriptionofDog[2] = Male;
Console.WriteLine("Dog name: {0}\nDog breed: {1}\nDog gender: {2}\n",descriptionofDog[0], descriptionofDog[1],
descriptionofDog[2]);
Dog has 3 string properties on it. Name, Breed and Gender.
When you built up the descriptionofDog object, you left off the quotes which told the compiler that these are objects, not strings.
Just put quotes around your string literals:
descriptionofDog[0] = "Tommy";
descriptionofDog[1] = "Bulldog";
descriptionofDog[2] = "Male";
To add to the #paq...'s answer. Following are the easier way to create new Dog object instead of using indexer.
Dog descriptionofDog = new Dog();
or
descriptionofDog = new Dog("Tommy", "Bulldog", "Male");
or
descriptionofDog =
new Dog() { Name = "Tommy", Breed = "Bulldog", Gender = "Male" };

Categories