How to allow only certain parameters in method in c# - c#

is there a way to only allow certain values as parameters in a method. Not at runtime, I mean it already shows an error when coding. For example:
I have this method:
public bool addPoints(Guid userId, uint amount)
{
...
}
It will be called multiple times in the code. But, I only want people to pass certain values. Those values are defined somewhere e.q. a class:
public class LeaderboardPoints
{
public const uint CREATE = 30;
public const uint REPLICATE = 15;
public const uint REMIX = 15;
public const uint COMMENT = 15;
}
Is there a way, that I can force the argument to be one of those props? or is there maybe another way to make sure that the right amount is passed?
Thanks!!

An enum is one option, but enums are just named integers - you can get them wrong and pass any integer (it is just more awkward).
Another way to make it much harder to pass in something wrong is to encapsulate the values in a type, using a non-public constructor to make it impossible (without reflection at least) to create invalid options. For example:
public bool AddPoints(Guid userId, Points points)
{
// something += points.Value
}
//...
public readonly struct Points
{
public uint Value {get;}
private Points(uint value) => Value = value;
public static Points Create {get;} = new Points(30);
public static Points Replicate {get;} = new Points(15);
public static Points Remix {get;} = new Points(15);
public static Points Comment {get;} = new Points(15);
}
// ...
something.AddPoints(userId, Points.Create);
You could also optionally give things names, if it helps debugging.

As others have commented, you can define and use an enum type for this:
public enum LeaderBoardAction : uint
{
CREATE = 30,
REPLICATE = 15,
REMIX = 15,
COMMENT = 15
}
Then use Enum.IsDefined() to ensure no one passes arbitrary values in place of a defined enum label:
public bool AddPoints(Guid userId, LeaderBoardAction action)
{
if(!Enum.IsDefined(typeof(LeaderBoardAction), action))
throw new ArgumentException("Expected a valid leader board action", nameof(action));
// ...
}

or is there maybe another way to make sure that the right amount is passed?
Another approach is to take away the parameter and instead give named methods that they can call; you have just 4 options so it's certainly feasible to:
private bool AddPoints(Guid userId, uint amount)
{
...
}
public bool AddCreatePoints(Guid userId) => AddPoints(userId, LeaderboardPoints.CREATE);
public bool AddReplicatePoints(Guid userId) => AddPoints(userId, LeaderboardPoints.REPLICATE);
public bool AddRemixPoints(Guid userId) => AddPoints(userId, LeaderboardPoints.REMIX);
public bool AddCommentPoints(Guid userId) => AddPoints(userId, LeaderboardPoints.COMMENT);
As with any such things there's always that drag of how much you need to change when adding more options; for my money I would just use an enum and trust developers not to put crazy values in

Related

How not to return a mutable from a method and exposing class internals

I think that is a commonly asked question and I believe I have covered the bases well, as in how objects, structs or value types are passed between functions. Let us assume I have the following classes:
public class User{
public int xCoor{get;set}
public int yCoor{get;set}
}
public class UserManager{
Dictionary<int,User> userMapping = new Dictionary<int,User>();
public void AddUser(User user){//stuff}
public void RemoveUser(User user){//stuff}
public IEnumerable<User> GetAllUsers(){return userMapping.Values;}
}
public class UserConsumer{
private UserManager;
public void GetUserCoordinates(){
var listThatExposesInternals = UserManager.GetAllUsers().ToList();
listThatExposesInternals.Foreach(user => user.xCoor = user.yCoor = 0);
// Now that I altered the internals of the class, it is messed up
// I only want the consumer to be able read
}
}
How can I make sure that the internals of the User class stays intact. I am aware that for this problem it is also appropriate to expose xCoor and yCoor rather than the whole User class but most of the time I face the problem (need?) to return the reference to the class. I appreciate any advises.
There are a number of approaches you could take. If there is never a situation where xCoor and yCoor should be changed after User is instantiated, you could require the values in its constructor and make the setter private to ensure they can't be changed outside this class.
public class User
{
public User(int xCoor, int yCoor)
{
this.xCoor = xCoor;
this.yCoor = yCoor;
}
public int xCoor{get; private set;}
public int yCoor{get; private set;}
}
If you do need User to be mutable, but want to discourage changing the properties under certain circumstances, you could create an interface for User to implement that only has getters on those properties. However, people could still cast their IReadableUser as a User and access those properties.
Another option would be to create a new class that wraps a User object, and only exposes getters which read the properties from an actual User instance, but cannot set those same properties. This option could be combined with the IReadableUser approach above, to hide the implementation details of the returned object while still preventing people from casting the object to a User to change its values.
public interface IReadOnlyUser
{
int xCoor {get;}
int yCoor {get;}
}
internal class ReadOnlyUser : IReadOnlyUser
{
private readonly User user;
public ReadOnlyUser(User user)
{
this.user = user;
}
public int xCoor{get { return this.user.xCoor; }}
public int yCoor{get { return this.user.yCoor; }}
}
public IEnumerable<IReadOnlyUser> GetAllUsers()
{
return userMapping.Values
.Select(u => (IReadOnlyUser) new ReadOnlyUser(u));
}
Yet another option would be to allow the users to mutate the values you return, but ensure these values are copied values so the next time someone asks for the list of users they're seeing instances that haven't been changed.
public IEnumerable<User> GetAllUsers()
{
return userMapping.Values
.Select(u => new User { xCoor = u.xCoor, yCoor = u.yCoor });
}
If you had more reference-based values in your User class, your Select statement would need to create new instances of those as well. If that becomes burdensome, you might consider giving each class a copy constructor to ensure that the responsibility for copying values is consolidated into the part of code where it's most likely to be noticed and fixed when things change.
My apologies for marking this as an answer. Lost my previous account and am not having enough reputation to post this as a comment (not yet).
One way to do it is to make all the Properties readOnly and set the values through the constructor. However, I am not sure if that works for you. If the behavior of class modifies the state of object, then that may throw a wrench in the wheel.
About the List, if the list returns a copy of the object (i.e. returns the copy of the List) then that should be good. If you want the individual objects to be not mutable, you have 2 options
Make copy of individual data containers (objects) instead of just copying the references (which this one does)
Force the data container to be non-mutable by exposing the properties as get only and make the setters as private.
Hope that helps.
Maybe removing the sets or making them private :
public class User{
public int xCoor{get;}
public int yCoor{get;}
}
or
public class User{
public int xCoor{get; private set;}
public int yCoor{get; private set;}
}
There are a few ways. I am suspecting that you merely need to remove the "set" from each set/get.
you could have:
public class User
{
protected int _xCoor = 0;
protected int _yCoor = 0;
public int xCoor
{
get { return _xCoor; }
}
public int yCoor
{
get { return _yCoor; }
}
}
In my example case, in order to change the values of _xCoor and _yCoor, you would have to created a class that inherits from the User class.

How to change constants with enum types?

I am working on ASP.NET MVC 4 application even though for this exact problem I think it's irrelevant. I am using EF 5 with Code First and I have entity Menu where a menu can be one of four different types. In my entity the type of the menu is declared as
public class Menu
{
//other properties
public int Type { get; set; }
}
and I have declared the different types as constants:
public static class MenuType
{
public const int Report = 10;
public const int Contract = 20;
public const int Taxes = 30;
public const int Interests = 40;
}
So if I want to get all Menus from Contract type I have this:
unitOfWork.MenuRepository.GetAll().Where(x => x.Type == MenuType.Contract).ToList();
even though it's working OK I want to change this with enum values, I've tried this:
public static class MenuType
{
public static enum Types
{
Report = 10,
Contract = 20,
Taxes = 30,
Interests = 40
}
}
But then to retrieve the same records from above my code is:
unitOfWork.MenuRepository.GetAll().Where(x => x.Type == (int)(MenuType.Types.Contract)).ToList();
a lot more unreadable and more to write as well. I've seen enums to be used in a way similar to the way I use the constants. I'm not sure, maybe I have to implement additional method.
However how I can change the constants with enum values without making the queries more unreadable? Maybe some additional method like MenuTypeValue("Contract")...
You would have to change the type of the property on the Menu class to match the new enum you declared
public class Menu
{
//other properties
public Types Type { get; set; }
}
This way you will be comparing an enum to an enum and your code will become nice and clear again.
If you cannot do that, you will be probably better off with sticking to int constants or you will need to cast between int and the enum type all the time.
Change type of property "Type" in your model to enum. EF5 smart enough to internally convert it to int.
Take a look here: http://msdn.microsoft.com/en-us/data/hh859576.aspx
If what you're trying to do is make it more readable, why not move the logic into the repository class.
So you could make a call:
unitOfWork.MenuRepository.GetAll(MenuType.Type.Contract);
Then in your repository just have a method such as:
List<...> GetAll(MenuType.Type type = null) { ... }
So now you can still call GetAll without the type as it's an optional param but in that method you can work out what you need to be returning. Your calls will look cleaner then.

Pass a variable from one file to another c#

I have two .cs files (Hex2Bin.cs and Program.cs) and I want to pass the variable end_addr from Program.cs to Hex2Bin.cs
My code in Program.cs:
class Program
{
enum to_exit {
exit_ok = 0,
exit_invalid_args,
exit_to_few_args,
exit_invalid_input_file,
exit_invalid_args_file,
exit_permission_denied,
exit_unexpected_eof
};
// class value holders
static String args_file_name = "";
static String in_u1_name = "";
static String in_u22_name = "";
static String out_name = "";
static short end_addr = 0x0000; // 4-digit Hexadecimal end address
static Byte[] version_code = { 0, 0, 0, 0 }; // 3 bytes version, 1 for extra info
}
Is there anyway I could do this? I know how to do it in c, but I'm very new to c#. Thanks.
C# doesn't work like C with respect to static variables. You can make the variable end_addr available outside the Program class by making it a public field. By default, fields are private.
public static end_addr = 0x0000;
And then it can be accessed like so:
var x = Program.end_addr;
However, I would recommend that you spend a little more time familiarizing yourself with C# idioms and conventions. It seems like your still thinking about C# in terms of C, and they are very different.
if you declare the variable like this:
public static short end_addr = 0x0000;
then from another class you can use it like this:
Program.end_addr
but don't do this, is not object oriented!
if your class Hex2Bin is used/invoked by the Main method of Program class, you should be able to pass your variables as input parameters of the methods you call or set them as properties of the classes/objects you use...
It's enough to mark end_addr as public like so
public static short end_addr = 0x0000;
Then you can access it from anywhere like this
Program.end_addr
It's a better practice though to use properties rather than fields for exposing data.
// Property
public static short end_addr { get; private set; }
// Constructor
public Program()
{
// Initialize property value.
end_addr = 0x0000;
}
You're talking about 'files' but what you really want to do is to pass data from your program's entry point (Program.cs) to a an object of a class (or method of static class) that will process the data, am I right?
If so, this should be pretty simple. You either have to modify your Program.cs and create an instance of the class (the one from Hex2Bin.cs) like this
...
Hex2Bin hex2bin = new Hex2Bin( end_addr );
...
I assume that the Hex2Bin is as follows:
public class Hex2Bin
{
private short endAddress;
public Hex2Bin( short endAddress )
{
this.endAddress = endAddress;
}
}
this will allow you to use the value of end_addr from Program.cs
Another approach is to pass it directly to the method that will make use of it:
Hex2Bin.Method(end_addr);
and in the Hex2Bin file:
public static void Method(short endAddress)
{
//... do the work here
}
Given your background in C, I think you may be mixing runtime with compile time issues.
However, in Hex2Bin.cs, you can create a static method that updates a static variable.
class Hex2Bin
{
static short end_addr = 0x0000;
static void updateEndAddr(short endAddr)
{
end_addr = endAddr;
}
}

C# Return Value, But keep getting error. Why?

Hello fellow stackoverflow members!
I'm very new to the C# language transfer from Java, Obj-C.
It looks pretty same as Java, but I have trouble issue in very simple thing.
I have created two individual class files, Class-A and Class-Human.
Specification for Class-A
it contains the static main method declared.And I've tried to create the new instance of Class-Human.
public static void main(String args[])
{
Human human = new Human("Yoon Lee", 99);
int expected = human.getNetID; //<-gets the error at this moment.
}
Specification for Class-Human
namespace Class-A
{
public class Human
{
public String name;
public int netId;
public Human(String name, int netId)
{
this.name = name;
this.netId = netId;
}
public int getNetID()
{
return netId;
}
}
Why can't copy over into local variable?
The compiler prompts me the error of
'Cannot convert method group of 'getNetID' delegate blah blah'
Thank you.
Change the method-call to:
int expected = human.getNetID();
In C#, method-calls require parantheses () containing a comma-separated list of arguments. In this case, the getNetID method is parameterless; but the empty parantheses are still required to indicate that your intention is to invoke the method (as opposed to, for example, converting the method-group to a delegate-type).
Additionally, as others have pointed out, there is a mismatch betweem the return-type of the method and the variable you're assigning its value to, which you're going to have to resolve somehow (change both the field-type and method return-type to int / parse the string as an integer, etc.).
On another note, C# natively supports properties for getter-setter semantics, so the idiomatic way of writing this would be something like:
//hyphens are not valid in identifiers
namespace ClassA
{
public class Human
{
// these properties are publicly gettable but can only be set privately
public string Name { get; private set; }
public int NetId { get; private set; }
public Human(string name, int netId)
{
this.Name = name;
this.NetId = netId;
}
// unlike Java, the entry-point Main method begins with a capital 'M'
public static void Main(string[] args)
{
Human human = new Human("Yoon Lee", 99);
int expected = human.NetId; // parantheses not required for property-getter
}
}
}
You're trying to use a method as if it's a property. You need parenthesis and to convert the string to int, or just make getNetID return an int.
I think you meant:
public int getNetID()
{
return netId;
}
Or better still, use automatic properties:
public int NetId {get; private set;} //Notice Making N in Net capital
And then:
int expected = human.getNetID();
This will do the trick (-:
It should be human.getNetID()
Edit: And yes, as Oren says - you should change your netId getter to return int. I assume that is what you want to do.
I see that netId is integer.
getNetID() return type is string.
return type is not matching.
netID is declared as an Int:
public int netId;
but your function getNetID returns a string:
public String getNetID()
Therefore, the body of getNetID makes no sense when it tried to return an int as a string:
return netId;
Human human = new Human("Yoon Lee", 99);
int expected = human.getNetID(); //<-gets the error at this moment.
you need to add parentheses after the method call.
The way you have it right now you are fetcing the function itself.

What is a good way to handle enums that need to be strings

I'm pretty sure an enum isn't what I want. What I want is a list of named items
CustomerLookup = "005",
CustomerUpdate = "1010"
The "005" and "1010" aren't my values, they are the values I need to send to a 3rd party that I have no control over. There are close to 500 of them. I just want my code to look nice.
Instead of
SendRequest("005");
I'd rather see
SendRequest(RequestType.CustomerLookup);
Anyone have any self-documenting ideas without getting all crazy in the code?
Anything wrong with:
public static class RequestType
{
public static readonly string CustomerLookup = "005";
// etc
}
or
public static class RequestType
{
public const string CustomerLookup = "005";
// etc
}
? Or if you want more type safety:
public sealed class RequestType
{
public static readonly RequestType CustomerLookup = new RequestType("005");
// etc
public string Code { get; private set; }
private RequestType(string code)
{
this.Code = code;
}
}
That will basically give you a fixed set of values (the constructor is private, so outside code can't create different instances) and you can use the Code property to get at the related string value.
How about using some kind of associative array?
The way you are already doing it seems right to me.
You are clearly defining the requesttype values in your code without ambiguity and when you come to use them you have intellisense on your side.
I think the real issue is how to get the 500 values into your code without any tyypos!

Categories

Resources