I have my enum like this.
public enum Gender
{
Man = 1,
Woman = 2
}
And I use ASP MVC4 to display the choices in a drop down like this.
#Html.DropDownListFor(model => model.Gender, new SelectList(Enum.GetValues(typeof(Namespace.Models.Enum.Gender))))
This works like a charm, it display Man/Woman in the drop down.
My problem is that I would like to show different names on those enums in different contexts.
Like one context would be if you are a Mom or a Dad. I would like to use the gender enum as base, but display Mom/Dad instad of Man/Woman.
Another context would be Boy/Girl, I still would like to use the gender enum, but display a different text.
Is this possible in any way?
EDIT
I used Kevin's solution and also added another extention method like this.
public static List<KeyValuePair<string, int>> GetValues(IGenderStrategy genderStrategy)
{
Dictionary<string, int> arr = new Dictionary<string, int>();
foreach (Gender g in System.Enum.GetValues(typeof(Gender)))
arr.Add(g.ToValue(genderStrategy), (int)g);
return arr.ToList();
}
Which I used like this in my view.
#Html.DropDownListFor(model => model.Gender, new SelectList(Chores.Models.Enum.EnumExtentions.GetValues(new Chores.Models.Enum.ParentStrategy()), "value", "key"))
I like #RakotVT answer of using an extension method but would extend it a bit further as you would need a new extension method for every situation which is not great.
I think a variation of the Strategy pattern might work better here (http://www.dofactory.com/Patterns/PatternStrategy.aspx)
Something like this -
public enum Gender
{
Man = 1,
Woman = 2
}
public interface IGenderStrategy
{
string DisplayName(Gender gender);
}
public class ParentStrategy : IGenderStrategy
{
public string DisplayName(Gender gender)
{
string retVal = String.Empty;
switch (gender)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
default:
throw new Exception("Gender not found");
}
return retVal;
}
}
public static class EnumExtentions
{
public static string ToValue(this Gender e, IGenderStrategy genderStategy)
{
return genderStategy.DisplayName(e);
}
}
public class Test
{
public Test()
{
Gender.Man.ToValue(new ParentStrategy());
}
}
Try to add Extentions class for your Enum. Here is an example of this class.
public static class EnumExtentions
{
public static string ToChildValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Boy";
break;
case Gender.Woman:
retVal = "Girl";
break;
}
return retVal;
}
public static string ToParentValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
}
return retVal;
}
}
Dunno if this is the neatest way, but how about something like:
#functions{
IEnumerable<SelectListItem> GetGenderSelectList(GenderContext genderContext)
{
return Enum.GetValues(typeof(Namespace.Models.Enum.Gender)).ToList().ConvertAll(x => new SelectListItem(){Value= x.ToString(), Text= GetGenderDescription(x, genderContext)});
}
string GetGenderDescription(Gender gender, GenderContext genderContext)
{
switch (GenderContext)
{
case Children: return gender == Man? "Boy" : "Girl";
case Parents: return gender == Man? "Dad" : "Mom";
default: return gender.ToString();
}
}
}
#Html.DropDownListFor(model => model.Gender, GetGenderSelectList(model.GenderContext))
Here 'GenderContext' is another Enum.
obviously you don't need to have those functions in the page functions - Could just add the list of items to the ViewBag before even getting to the view.
Related
I am wondering if there is some clever way to retrieve data from an enumerable using LINQ when individual values from multiple records are needed.
For example, let's say you have a person with three different phone fields:
public class Person
{
public Phone HomePhone { get; set; }
public Phone WorkPhone { get; set; }
public Phone CellPhone { get; set; }
}
...but the phone list is stored in a normalized format:
public enum PhoneType
{
Home, Work, Cell
}
public class Phone
{
public PhoneType Type { get; set; }
public string Number { get; set; }
}
static public IEnumerable<Phone> GetPhoneList()
{
yield return new Phone { Type = PhoneType.Home, Number = "8005551212" };
yield return new Phone { Type = PhoneType.Work, Number = "8005551313" };
yield return new Phone { Type = PhoneType.Cell, Number = "8005551414" };
}
If you needed to populate Person, you could write a loop, and get everything you need in one pass:
public static Person GetPerson1()
{
var result = new Person();
foreach (var ph in GetPhoneList())
{
switch (ph.Type)
{
case PhoneType.Home: result.HomePhone = ph; break;
case PhoneType.Work: result.WorkPhone = ph; break;
case PhoneType.Cell: result.CellPhone = ph; break;
}
}
return result;
}
But if you wanted to use LINQ, it seems like three passes may be needed:
public static Person GetPerson2()
{
return new Person
{
HomePhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Home ),
WorkPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Work ),
CellPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Cell )
};
}
Is there a clever way to use LINQ to get it all with only one pass over the enumeration?
Here is a link to a Fiddle if you'd like to explore my code.
(I am aware I could use a dictionary or other data structure to solve this particular problem; this is just an example.)
Normally, you can't do this in LINQ.
If you really want to, you can create a Foreach extension method and do the same as your GetPerson1 method.
public static class Ext
{
public static void Foreach<T>(this IEnumerable<T> e, Action<T> action)
{
foreach (T item in e)
{
action(item);
}
}
}
and then
public static Person GetPerson2()
{
var p = new Person();
var pl = GetPhoneList();
pl.Foreach(ph =>
{
switch (ph.Type)
{
case PhoneType.Home: p.HomePhone = ph; break;
case PhoneType.Work: p.WorkPhone = ph; break;
case PhoneType.Cell: p.CellPhone = ph; break;
}
});
return p;
}
But you really shouldn't. LINQ is meant to operate on IEnumerables (item by item), and LINQ functions should be without side effects, while your foreach loop and Foreach extension methods are only creating side effects, changing the state of the Person object.
And, besides, the fact that you need a 'clever way' should be an indication that this is not the way it's meant to be used :)
There's a great article by Eric Lippert with more details here: https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
If there is no guarantee that numbers from the same person come in a sequence then you have to enumerate the list until you find all the numbers. It does not seem to me this is a good candidate for LINQ, whose purpose is to make the code more readable. Your foreach is just fine, and I would just break the loop when all numbers are found.
If you want to enumerate all the persons, and not just one then Dictionary approach is probably most effective. GroupBy internally uses a dictionary and you can use GroupBy to collect all the numbers belonging to a person, and then Aggregate to make a Person out of them. Let's assume there is some property Phone.PersonID, and also Person.PersonID, then you would have something like this:
GetPhoneList()
.GroupBy(x => x.PersonID)
.Select(x => x.Aggregate(new Person() { PersonID = x.Key },
(person, phone) =>
{
switch (phone.Type)
{
case PhoneType.Home: person.HomePhone = phone; break;
case PhoneType.Work: person.WorkPhone = phone; break;
case PhoneType.Cell: person.CellPhone = phone; break;
}
return person;
}));
I assume here that GetPhoneList() returns all the phones of all persons.
So I have been looking around and it seems like the correct answer to getting rid of big switch case is polymorphism, but I just can't figure out how I can change this from conditionnal to poplymorphic. Is this the right solution here?
Console.WriteLine(#"Menu");
Console.WriteLine(#"1.Create Account");
Console.WriteLine(#"2.ATM");
Console.WriteLine(#"3.Account info");
Console.Write(#"Please enter your selection: ");
var menuChoice = int.Parse(Console.ReadLine());
switch (menuChoice)
{
case 1:
atm.CreateAccount();
break;
case 2:
//Console.WriteLine(#"1.Deposit Or Withdraw");
Console.WriteLine(#"1.Deposit");
Console.WriteLine(#"2.Withdraw");
Console.Write(#"Please enter your selection: ");
var atmMenuChoice = int.Parse(Console.ReadLine());
switch (atmMenuChoice)
{
case 1:
atm.Deposit();
break;
case 2:
atm.Withdraw();
break;
default:
Console.WriteLine(#"Invalid selection!");
break;
}
break;
case 3:
atm.AccountInfo();
break;
default:
Console.WriteLine(#"Invalid selection!");
break;
}
}
In situations like this I tend to use a Dictionary<string, Action> to lookup what to do for each input.
Something like:
var actions = new Dictionary<string, Action>
{
{ "1", atm.CreateAccount }
{ "2", AtmSelection } //This would do the same as below with the atmActions dictionary
{ "3", atm.AccountInfo }
}
var atmActions = new Dictionary<string, Action>
{
{ "1", atm.Deposit }
{ "2", atm.Withdraw }
}
var input = GetInput(); //From stdin as you do currently
if (actions.TryGetValue(input, out var action))
{
action();
}
else
{
Console.WriteLine("Invalid Selection");
}
I personally find this easier to read than a massive nested switch statement
The preference for polymorphism over a switch usually applies when you're using some sort of serialization framework. Imagine that your int is the serialized representation of a member of a class of singletons, all of which have a particular method that operates on (or visits) your atm object. Then you could deserialize the instance and call that method:
var foo = deserializer.deserialize(intVal);
foo.doStuff(atm);
There's still a switch involved, but it's inside the serialization framework and you don't have to maintain it. If you want to implement a similar pattern without a serialization framework, you'll have to write the switch yourself. The benefit is that you can separate the switch from the rest of the logic:
Foo GetFoo(int type) {
// switch on type
}
var foo = GetFoo(intVal);
foo.doStuff(atm);
This pattern developed in languages that do not (or did not) have function pointers or the equivalent. In languages that do have function pointers, a map of int values to functions as suggested in another answer would essentially accomplish the same thing.
I may have gone a little crazy here, but this works in a similar way to Scott's answer.
static IEnumerable<MenuItem> RootMenu;
static void Main(string[] args)
{
RootMenu = BuildRootMenu();
MenuItem.DisplayMenu(RootMenu, new Atm());
}
/// <summary>
/// Creates the entire menu
/// </summary>
static IEnumerable<MenuItem> BuildRootMenu()
{
MenuItem item1 = new MenuItem() { DisplayText = "Create Account", AtmAction = (a) => a.CreateAccount() };
MenuItem item2_1 = new MenuItem() { DisplayText = "Deposit", AtmAction = (a) => a.Deposit() };
MenuItem item2_2 = new MenuItem() { DisplayText = "Withdraw", AtmAction = (a) => a.Withdraw() };
MenuItem item2 = new MenuItem() { DisplayText = "ATM", AtmAction = (a) => MenuItem.DisplayMenu(new List<MenuItem> { item2_1, item2_2 }, a) };
MenuItem item3 = new MenuItem() { DisplayText = "Account Info", AtmAction = (a) => a.CreateAccount() };
return new List<MenuItem> { item1, item2, item3 };
}
class MenuItem
{
public String DisplayText;
public Action<Atm> AtmAction = null;
public void Execute(Atm atm)
{
AtmAction(atm);
DisplayMenu(RootMenu, atm);
}
public static void DisplayMenu(IEnumerable<MenuItem> menuItems, Atm atm)
{
int i = 1;
foreach (var mi in menuItems)
{
Console.WriteLine(i + ": " + mi.DisplayText);
i++;
}
var rk = Console.ReadKey();
menuItems.ToArray()[int.Parse(rk.KeyChar.ToString()) - 1].Execute(atm);
}
}
class Atm
{
public void Deposit()
{
Console.WriteLine("Ran Deposit");
}
public void Withdraw()
{
Console.WriteLine("Ran Withdraw");
}
public void CreateAccount()
{
Console.WriteLine("Ran CreateAccount");
}
public void AccountInfo()
{
Console.WriteLine("Ran AccountInfo");
}
I am trying to convert the items in a list in to a string. but every time I convert it or display it all is shows is "TwitchIrcChar.user". If some one could help with this, it would be very helpful. sorry if noob question, but im new to lists. ive tried using convert.ToString and userlist.tostring. both gave the same output
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
namespace TwitchIrcChat
{
class User
{
static Random random = new Random();
public string UserName { get; set; }
public SolidColorBrush Color { get; set; }
public bool IsMod { get; set; }
public User(string username)
{
IsMod = false;
UserName = username;
randomColor();
}
public void setColor(SolidColorBrush color)
{
Color = color;
}
private void randomColor()
{
var temp = Brushes.White;
int randomColor = random.Next(0, 10);
switch (randomColor)
{
case 0:
temp = Brushes.Blue;
break;
case 1:
temp = Brushes.Green;
break;
case 2:
temp = Brushes.Red;
break;
case 3:
temp = Brushes.Purple;
break;
case 4:
temp = Brushes.Orange;
break;
case 5:
temp = Brushes.Yellow;
break;
case 6:
temp = Brushes.Gold;
break;
case 7:
temp = Brushes.Teal;
break;
case 8:
temp = Brushes.Cyan;
break;
case 9:
temp = Brushes.LightBlue;
break;
case 10:
temp = Brushes.Pink;
break;
}
Color = temp;
}
}
class UserList
{
public moderation q = new moderation();
public List<User> userList { get; set; }
public UserList()
{
userList = new List<User>();
}
public void Add(string userName)
{
bool isInList = false;
foreach (var item in userList)
{
if (item.UserName.Equals(userName))
{
isInList = true;
break;
}
}
if (!isInList)
{
var tempUser = new User(userName);
userList.Add(tempUser);
}
}
public void Remove(string userName)
{
int userLocation = -1;
for (int i = 0; i < userList.Count; i++)
{
if (userName.Equals(userList[i].UserName))
{
userLocation = i;
break;
}
}
try
{
userList.RemoveAt(userLocation);
}
catch (Exception)
{
}
}
public SolidColorBrush getColor(string username)
{
var temp = Brushes.White;
foreach (var item in userList)
{
if (item.UserName.Equals(username))
{
temp = item.Color;
}
}
return temp;
}
public void setColor(string username, string color)
{
if (userList.Count(s => s.UserName == username) == 0)
{
Add(username);
}
var user = userList.First(s => s.UserName == username);
var converter = new BrushConverter();
var brush = (SolidColorBrush)converter.ConvertFromString(color);
user.Color = brush;
}
public void Clear()
{
userList.Clear();
}
public void list()
{
Console.WriteLine("qweqweqweqwe");
for (int i = 0; i < userList.Count; i++) // Loop through List with for
{
Console.WriteLine(userList[i].ToString());
Console.WriteLine("qweqweqweqwe");
}
}
public void AddMod(string userName)
{
foreach (var item in userList)
{
//string a = item.ToString();
//q.writeToFile(a);
if (item.UserName.Equals(userName))
{
item.IsMod = true;
}
}
}
}
}
You could override ToString like others have suggested or if UserName is all you rally want you just do.
Console.WriteLine(userList[i].UserName.ToString());
or
Console.WriteLine(userList[i].UserName);
since its already a string
You have to override ToString() method in your class and return the desired string in that method. For instance if you want to return UserName when ToString() is called on an instance of User, you can do it like this:
public class User
{
public string UserName {get;set;}
public override string ToString()
{
return UserName;
}
}
If you don't do this, the default ToString() will return the name of the object's type.
This has nothing to do with lists, and everything to do with how you represent a custom object as a string.
The default behavior for .ToString() is exactly what you're seeing, outputting the name of the class. .NET has no way of intuitively knowing what you mean when you want to see an object as a string. You need to explicitly provide that logic by overriding .ToString() on your object.
For example, if you just want to see the user's name, it could be something as simple as:
public override string ToString()
{
return UserName;
}
Essentially, the question you need to ask yourself is, "Am I outputting a property on the User, or am I outputting the User itself?" If the latter, you'd definitely want to encapsulate that logic into a .ToString() override, since that logic may change over time. For example, if you ever want the string representation of a User to also show if the User is a "mod" (say, for example, with a * character), you would just add that in the override:
public override string ToString()
{
return string.Format("{0} {1}",
UserName,
IsMod ? "(*)" : string.Empty);
}
The default behavior of ToString() (inherited from System.Object) is to display the type name. If you want to change this behavior you must override ToString:
class User
{
...
public override string ToString()
{
return UserName + (IsMod ? " (moderator)" : "");
}
}
ToString is used automatically by Console.WriteLine, so you simply call it like this:
Console.WriteLine(userList[i]);
You can also add objects directly to listboxes for instance, as those use ToString as well in order to display the items.
listBox1.Items.Add(user);
To give some background I'm trying to solve the Project Euler Problem 54 involving poker hands. Though there's infinite approaches to this. What I would like to do is enumerate through a list of strings, for example:
{ "8C", "TS", "KC", "9H", "4S" };
I would like to "get" an instance of class card with properties value, and suit, for each respective string. I've not yet utilized get/set so maybe there is an obvious approach to this I'm missing.
Ultimately I would like to have a list of objects type Card, I don't mind building all the card's ahead of time, such that "2H" returns an instance of type Card where suit = Hearts, and value = 2, for example.
I know this code is wrong, but it should give an idea of what I'm trying to do. Any suggestions would be appreciated.
class Card
{
public string suit;
public int value;
public string cardname
{
get
{
if (cardname == "2H") Card TwoH = new Card();
TwoH.suit = "Hearts"
TwoH.value = 2;
return TwoH;
}
}
}
Why not make a constructor that fills suit and value based on a string parameter
public Card(string name)
{
switch(name)
{
case "2H":
this.suit = "Hearts";
this.value = 2;
break;
//...
}
}
This might not be the exact solution you seem to be asking for but if the values you'll be getting (eg 2H, 3C etc) are all 2 characters long, then you can try this:
public class Card
{
public string suit { get; set; }
public int value { get; set; }
public static Card GetCard(string cardName)
{
string tmpSuit;
int tmpValue;
char[] cardNameParts = cardName.ToCharArray();
switch(charNameParts[0])
{
case "A":
tmpValue = 1;
break;
case "2":
tmpValue = 2;
break;
...
}
switch(charNameParts[1])
{
case "H":
tmpSuit= "Hearts";
break;
case "C":
tmpSuit= "Clubs";
break;
...
}
return new Card() { suit = tmpSuit, value = tmpValue };
}
}
I would do it like that:
public class Card
{
public string Suit { get; set; }
public int Value { get; set; }
public static Card FromString(string s)
{
if (s == "2H") return new Card() { Suit = "Hearts", Value = 2 };
else if (s == "....")
...
else return null;
}
}
I have converted your suit and value field into properties and instead of some getter method which in your case wouldn't work I have added a static method.
You can use it like this Card card2H = Card.FromString("2H");
Maybe use two switch statements, first
switch (cardname[0])
{
...
}
then
switch (cardname[1])
{
...
}
Before that, check that cardname.Length == 2. In each switch, have a default section where you throw an exception in case the char value doesn't make sense.
I have the following code:
switch(first)
{
case 'A':
vm.Content = contentService.Get("0001000", vm.RowKey);
return View("Article", vm);
case 'F':
vm.Content = contentService.Get("0002000", vm.RowKey);
return View("FavoritesList", vm);
}
'A' refers to a page type of Article with a key of "0001000"
'F' refers to a page type of Favorite with a key of "0002000"
Is there a way in C# that I could avoid having to code in the keys as a string?
Some way that would allow me to code in by the key abbreviation or name
and then have C# convert this to a string?
Can I use Enum for this? This seems ideal but I am not sure how to set up an Enum.
You may think about dictionary (if I right understood your question)
//class for holding relation between the code and page name
public class Data
{
public string Code {get;set;}
public string PageName {get;set;}
}
var dic = new Dictionary<string, Data >{
{"A", new Data{Code="0001000", PageName = "Article"},
{"F", newe Data{Code="0002000", PageName="FavoritesList"}
}
and after use it like:
Data dt = null;
if(dic.TryGetValue(first, out dt)) { // *first* is parameter you use in switch
vm.Content = contentService.Get(dt.Code, vm.RowKey);
return View(dt.PageName, vm);
}
You can use enums and use extension methods to allow an alternative text output.
The enum:
public enum PageTypes
{
A,
F
}
The extension method (needs to be in the top level of your project):
public static class Extensions
{
public static string getText(this PageTypes type)
{
switch (type)
{
case PageTypes.A:
return "0001000";
case PageTypes.F:
return "0002000";
default:
return null;
}
}
}
And your code:
PageTypes type;
//assing value to type:
//type = ...
var vm.Content = contentService.Get(type.getText(), vm.RowKey);
switch (type)
{
case PageTypes.A:
return View("Article", vm);
case PageTypes.F:
return View("FavoritesList", vm);
}
Now you do not need to use strings to retrieve the values.
I would put the keys in a dictionary, e.g.
var keyData = new Dictionary(char,string);
keyData.Add('A',"0001000");
keyData.Add('A',"0001000");
keyData.Add('F',"0002000");
You could then reference them using
var value = keyData['A'];