This question already has answers here:
Check inside method whether some optional argument was passed
(10 answers)
Closed 5 years ago.
I want to let a user specify a custom color for a class in the constructor via passing in RGBA bytes. If they do not specify a custom color, a default color from the application settings will be used. If the Alpha is left out, however, assume fully opaque.
What I would like:
public MyClass(byte r_col = -1, byte g_col = -1, byte b_col = -1, byte a_col = 255)
{
if (r_col == -1 | g_col == -1 | b_col == -1)
{
// use default color
}
else
{
this.color = System.Windows.Media.Color.FromArgb(a_col, r_col, g_col, b_col);
}
}
However, there is no "wrong" value for a byte (-1 is invalid), so I am unable to detect if a byte was actually passed into the function. What options do I have? I'd like to avoid function overloading if possible.
Function overloading is much more beautiful in this case:
public MyClass()
{
//Default color
}
public MyClass(byte r_col, byte g_col, byte b_col)
{
color = Color.FromArgb(r_col, g_col, b_col);
}
public MyClass(byte a_col, byte r_col, byte g_col, byte b_col)
{
color = Color.FromArgb(a_col, r_col, g_col, b_col);
}
Of course it is possible to do it without (as Micheal proofed), but it's (P.Kouverakis mentioned) not good API design. Because if you let the user type in parameters, which aren't allowed, this may result in difficult to trace bugs. Never fear more work for a greater result - so in this case, use function overloads.
I suppose this is one of the reasons why C# has nullable types. The following code worked well by using a nullable type to check if usable arguments were passed in.
public MyClass(byte? r_col = null, byte? g_col = null, byte? b_col = null, byte a_col = 255)
{
if (r_col == null | g_col == null | b_col == null)
{
// use default color
}
else
{
System.Windows.Media.Color.FromArgb(a_col,
r_col.GetValueOrDefault(),
g_col.GetValueOrDefault(),
b_col.GetValueOrDefault());
}
}
This is an answer to my own question - other suggestions are also appreciated.
Related
This question already has answers here:
What does the [Flags] Enum Attribute mean in C#?
(14 answers)
Closed 7 years ago.
Say I have an enum defined like so:
[Flags]
public enum LogLevel
{
None = 1,
Pages = 2,
Methods = 4,
Exception =8
}
and a class like:
public static class Log
{
public static LogLevel Level = LogLevel.Methods | LogLevel.Pages;
public static void EnterPage([CallerFilePath]string filePath = "")
{
if (Level == //What value here to check if Level includes Pages?)
{
//Log
}
}
What value do I need to equate Level to to check whether the enum includes Pages?
First of all, flags must have a None = 0 value, because otherwise there's no way to represent a 0 mask (i.e. no value).
Once you've fixed it, you can check if some enumeration value is in some given flag using Enum.HasFlag or the bit-wise & operator:
Level.HasFlag(LogLevel.Pages);
...or:
(Level & LogLevel.Pages) == LogLevel.Pages
Finally, when you implement a flags enumeration, usually enumeration identifier is expressed in plural. In your case, you should refactor LogLevel to LogLevels.
Why &?
Each enumeration value represents a bit in a complete mask. For example, None would be 0, but it could be also represented as 0, 0, 0 where each 0 is one of possible enumeration values in LogLevels. When you provide a mask like LogLevels.Pages | LogLevels.Methods, then the mask is 1, 1, 0.
In order to check if Pages is within the mask, you use a logical AND comparing the mask with one of possible enumeration values:
1, 1, 0 (LogLevels.Pages | LogLevels.Methods)
1, 0, 0 AND (LogLevels.Pages)
--------
1, 0, 0
1 and 1 (true and true == true)
1 and 0 (true and false == false)
0 and 0 (false and false == false).
The whole AND is like isolating the tested enumeration value. If the resulting mask equals the enumeration value, then the mask contains the enumeration value.
Some OP concern
OP said in some comment:
Just a quick question on zero value. here it states that You cannot
use the None enumerated constant in a bitwise AND operation to test
for a flag because the result is always zero. Does that mean if I have
a 0 value I cannot use & and must use HasFlag?
None (i.e. 0) won't be a member of a mask, because 0 doesn't exist. When you produce a mask you're using the OR | logical operator, which is, at the end of the day, an addition.
Think about 1 + 0 == 1. Would you think checking if 0 is within 1 is ever possible?
Actually, the None = 0 value is required to be able to represent and empty mask.
Finally, HasFlag does the AND for you, it's not a magical solution, but a more readable and encapsulated way of performing the so-called AND.
like this
if (Level.HasFlag(LogLevel.Pages))
{
//todo
}
This question already has answers here:
Why does C# 3 allow the implicit conversion of literal zero (0) to any Enum?
(5 answers)
Closed 7 years ago.
I want to parse a binary file.
I have 3 valid formats. in the binary file the format is represented by short. but it can be only 0,1,2
I created enum to describe these formats.
When i wrote this code i saw this compiler error:
Operator '>' cannot be applied to operands of enum and int.
public enum FormatType
{
Type0 = 0,
Type1 = 1,
Type2 = 2
}
private FormatType _format;
public FormatType Format
{
get { return _format; }
set
{
// red line under value > 2.
if (value < 0 || value > 2) throw new FileParseException(ParseError.Format);
_format = value;
}
}
but there is no problem with value < 0.
later i find out that i can compare enum with 0 but not with other numbers.
simply to fix this problem i can cast int to enum.
value > (FormatType)2
But no need to cast when comparing with 0 why?
value < 0
you need to cast the enum to int where you are using it as an int:
public FormatType Format
{
get { return _format; }
set
{
// red line under value > 2.
if (value < 0 || (int)value > 2) throw new FileParseException(ParseError.Format);
_format = value;
}
}
Edit:
literal zero will always be implicitly convertible to any enum to ensure your are able to initialize it to its default value (even if there is no enum with value 0)
Found these links that explain it better:
http://blogs.msdn.com/b/ericlippert/archive/2006/03/29/the-root-of-all-evil-part-two.aspx
http://blogs.msdn.com/b/ericlippert/archive/2006/03/28/563282.aspx
This question already has answers here:
Byte enum comparison in C#
(3 answers)
Closed 8 years ago.
I have set up a enum like this:
public enum ServerCommands:byte {
New = 0,
Join = 1,
}
And I want to use it like this:
byte command = buffer[0];
if (command == ServerCommands.Join) // Error: Operator == cannot be operands of type 'byte' and 'ServerCommands'.
Why is this not possible to do and how can I make it work? They are both of type byte.
You still need to cast from byte to ServerCommands! This is not done automatically. Assinging numbers to enum values is just for clarity when casting enum to int or other permitted numeric types.
Assigning numeric values to enum values does not change their type to the numeric type! You can cast any enum value to an int, as all enums (if not declared otherwise) can be cast to int, the first enum value having int value 0.
public enum MyEnum
{
First,
Second
}
is equal to
public enum MyEnum : int
{
First = 0,
Second
}
The feature of numbering enum values is required if the number is not linear, as in:
public enum ErrorCodes: int
{
Success = 0,
FileNotFound = 1,
MissingRights = 5,
WhatTheHeck = 18
}
You better state conversions explicitly:
if (command == (byte) ServerCommands.Join)
or even better:
if ((ServerCommands) command == ServerCommands.Join) //always convert to the more restrictive type.
This is a precaution to prevent one from comparing values without knowing that the objects are from a different type.
Equality means that both objects have the same type. This is not the case. ServerCommands extend from byte. Thus a byte is not per se a valid ServerCommands object...
Furthermore the : byte is more used as an explicit encoding. Having the same binary encoding does not imply two objects are the same. For instance 14.25f and 0x41640000 are binary the same...
You probably need to cast your enum:
if (command == (byte)ServerCommands.Join)
Is the use of implicit enum fields to represent numeric values a necessarily bad practice?
Here is a use case: I want an easy way to represent hex digits, and since C# enums are based on integers, they seem like a natural match. I don't like a char or a string here, because I have to explicitly validate their values. The problem with enums is that digits [0-9] are not valid field identifiers (with good reason). It occurred to me that I don't need to declare the digits 0-9, because they are implicitly present.
So, my hex digit enum would look like:
public enum Hex : int {
A = 10,
B = 11,
C = 12,
D = 13,
E = 14,
F = 15
}
So, I could write Tuple<Hex,Hex> r = Tuple.Create(Hex.F,(Hex)1);, and r.Item1.ToString() + r.Item2.ToString() would give me "F1". Basically, my question is that if the ToString() value of the numeric constant is what I want to name the enum field, why is it problematic to omit the declaration entirely?
An alternative representation as an enum could have the fields declared with some prefix, such as:
public enum Hex : int {
_0 = 0,
_1 = 1,
_2 = 2,
_3 = 3,
_4 = 4,
_5 = 5,
_6 = 6,
_7 = 7,
_8 = 8,
_9 = 9,
A = 10,
B = 11,
C = 12,
D = 13,
E = 14,
F = 15
}
The problem is that the above example would give me "F_1" instead of "F1". Obviously, this is easy to fix. I'm wondering if there are additional problems with the implicit approach that I am not considering.
It's bad practice because it's a clever trick that's surprising to the people who read your code. It surprised me that it actually worked, it had me saying wtf. Remember the only valid measurement of code quality:
Clever tricks don't belong in code that's meant to be read and maintained by others. If you want to output a number as hex, convert it to a hex string using the normal String.Format("{0:X}", value)
This is a fundamentally broken way to handle hex. Hex is a human interface detail. It is always a string, a representation of a number. Like "1234" is a representation of the value 1234. It happens to be "4D2" when represented in hex but the number in your program is still 1234. A program should only ever concern itself with the number, never with the representation.
Converting a number to hex should only happen when you display the number to human eyes. Simple to do with ToString("X"). And to parse back from human input with TryParse() using NumberStyles.HexNumber. Input and output, at no other point should you ever deal with hex.
I would define a struct for HexDigit. You can add HexDigit 'A' to 'F' as static constants (or static readonly fields).
You can define implicit converters to allow conversion of integers 0-9, conversion to integers, and you can override ToString() to make you Tuples look nice.
That will be much more flexible than an enum.
In my opinion, it is a bad practice. If you need Hex representation, simply create a helper class that handles all the operations you require.
As this article suggests, these code snippets will help in creating the helpers:
// Store integer 182
int decValue = 182;
// Convert integer 182 as a hex in a string variable
string hexValue = decValue.ToString("X");
// Convert the hex string back to the number
int decAgain = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
The reason I believe it's bad practice is because it's not object oriented, and it runs into the problem of relying on the enum to translate all the values hard-coded - which is bad. If you can avoid hard coding anything, it's always a step in the right direction. Also, a helper class is extensible and can be improved over time for additional functionality.
That being said, I DO like the simplicity of enums, but, again, that doesn't supersede the need to keep things OO in my opinion.
I'm not sure what you're actually trying to accomplish here, but if you're looking to limit something to two hexadecimal digits, why wouldn't you just declare it as a byte? While your enum hack is clever, I don't actually see the need for it. It's also likely to be misundertstood if passed to another programmer without explanation as your use of undeclared values against your enum is counterintuitive.
Regarding number bases and literal representations, an integer in computing isn't base-10 or base-16 natively, it's actually base-2 (binary) under the covers and any other represenations are a human convenience. The language already contains ways to represent literal numbers in both decimal and hexadecimal format. Limiting the number is a function of appropriately choosing the type.
If you are instead trying to limit something to any arbitrary even quantity of hexadecimal digits, perhaps simply initializing a byte array like this would be more appropriate:
byte[] hexBytes = new byte[3] { 0xA1, 0xB2, 0xC3 };
Also, by keeping your value as a regular numeric type or using a byte array rather than putting it into Tuples with enums, you retain simple access to a whole range of operations that otherwise become more difficult.
Regarding limiting your numbers to arbitrary odd quantities of hexadecimal digits, you can choose a type that contains at least your desired value + 1 digit and constrain the value at runtime. One possible implementation of this is as follows:
public class ThreeNibbleNumber
{
private _value;
public ushort Value
{
get
{
return _value;
}
set
{
if (value > 4095)
{
throw new ArgumentException("The number is too large.");
}
else
{
_value = value;
}
}
}
public override string ToString()
{
return Value.ToString("x");
}
}
In one of your comments on another answer, you reference the idea of doing CSS colors. If that's what you desire a solution like this seems appropriate:
public struct CssColor
{
public CssColor(uint colorValue)
{
byte[] colorBytes = BitConverter.GetBytes(colorValue);
if (BitConverter.IsLittleEndian)
{
if (colorBytes[3] > 0)
{
throw new ArgumentException("The value is outside the range for a CSS Color.", "s");
}
R = colorBytes[2];
G = colorBytes[1];
B = colorBytes[0];
}
else
{
if (colorBytes[0] > 0)
{
throw new ArgumentException("The value is outside the range for a CSS Color.", "s");
}
R = colorBytes[1];
G = colorBytes[2];
B = colorBytes[3];
}
}
public byte R;
public byte G;
public byte B;
public override string ToString()
{
return string.Format("#{0:x}{1:x}{2:x}", R, G, B).ToUpperInvariant();
}
public static CssColor Parse(string s)
{
if (s == null)
{
throw new ArgumentNullException("s");
}
s = s.Trim();
if (!s.StartsWith("#") || s.Length > 7)
{
throw new FormatException("The input is not a valid CSS color string.");
}
s = s.Substring(1, s.Length - 1);
uint color = uint.Parse(s, System.Globalization.NumberStyles.HexNumber);
return new CssColor(color);
}
}
I don't particularly see why you would want to do this, but you could use the Description attribute on each of your enum values to get rid of the _ and create some kind of static function that allows you to get one of your enum values easily like Hex(15) -> 'F'.
public enum Hex {
[Description("0")] _0 = 0,
...
}
In my application, I have something like:
public enum Locations {
LocationA,
LocationB,
LocationC
}
private List<Locations> _myLocations;
public Int64 PackedLocations {
get {
return PackEnumList(this._myLocations);
}
}
So: an enum (backed by int), a List of those enum values, and a read-only property which returns the result of a method I've left out so far.
That method, PackEnumList, is meant to give me a 64-bit integer where each BIT denotes whether the corresponding enum value was selected or not in a list of unique enum values. So in my example above, if _myLocations has only one item: {Locations.LocationA}, the result would be 1 (binary: ...00001), if we then add Locations.LocationC to that list, the result would be 5 (binary: ...000101). The implementation isn't important right now (but I'll include it below for completion/interest/feedback), but the signature of that method is:
public Int64 PackEnumList(List<Enum> listOfEnumValues) {
...
}
When I compile, I get an error that "the best overloaded method ... has some invalid arguments".
I'm guessing this is because _myLocations is being seen as a List of int values, but I'd like PackEnumList() to work even if the enumeration being used were backed by something else, if possible.
Is there a more appropriate way to make a method which will accept a List/Collection of any enumeration?
For completeness, here's the rest of what I'm trying to do (these are static because they're in a shared utility class). These are completely untested yet (since I can't get past the compile error when calling the pack method), so take them with a grain of salt. And there might be a better way to do this, I'm doing this half to solve an interesting problem, and half because I think it is an interesting problem.
public static Int64 PackEnumList(List<Enum> listOfEnumValues) {
BitArray bits = new BitArray(64, defaultValue: false);
foreach (var value in listOfEnumValues) {
// get integer value of each Enum in the List:
int val = Convert.ToInt32(value);
if (val >= 64) {
// this enum has more options than we have bits, so cannot pack
throw new Exception("Enum value out of range for packing: " + val.ToString());
}
bits[val] = true;
}
var res = new Int64[1];
bits.CopyTo(res, 0);
return res[0];
}
// (this method is a little farther from the ideal: the resulting list will need
// to be matched by the caller to the appropriate List of Enums by casting
// each Int32 value to the Enum object in the list)
public static List<Int32> UnpackEnumList(Int64 packedValue) {
string binaryString = Convert.ToString(packedValue, 2);
List<Int32> res = new List<Int32>();
for (int pos = 0; pos < binaryString.Length; pos++) {
if (binaryString[binaryString.Length - pos - 1] == '1') {
// bit is on
res.Add(pos);
}
}
return res;
}
Is there a more appropriate way to make a method which will accept a List/Collection of any enumeration?
Within straight C#? Nope. But you can fudge it...
I have a project called Unconstrained Melody which allows you to make a generic method with a constraint of "T must be an enum type" or "T must be a delegate type". These are valid constraints at the IL level, but can't be expressed in C#
Basically Unconstrained Melody consists of two parts:
A library of useful methods with those constraints, where the source code is written using valid C# which doesn't actually represent those constraints, but uses a marker interface
An IL-rewriting project (ugly but servicable) which converts those constraints into the real "unspeakable" ones
(The expectation is that users of the library would just use the rewritten binary.)
It sounds like you could use the latter part of the project for your code here. It won't be terribly pleasant, but it would work. You might also find the library part useful.
As a side thought, you might want to consider using a [Flags]-style enum instead:
[Flags]
public enum Locations {
LocationA = 1 << 0,
LocationB = 1 << 1,
LocationC = 1 << 2
}
Change your method signature to public Int64 PackEnumList(IEnumerable<Enum> listOfEnumValues)
And then call it like following:
public Int64 PackedLocations
{
get { return PackEnumList(this._myLocations.Cast<Enum>()); }
}
A List<Enum> is not a List<Locations> nor a List<Int32>. Use a generic method to handle the list:
public static void PackEnumList<T>(IEnumerable<T> list) where T : IConvertible
{
foreach (var value in list)
int numeric = value.ToInt32();
// etc.
}
I'd change your signature method to:
public Int64 PackEnumList<T>(IEnumerable<T> listOfEnumValues) where T : struct, IFormattable, IConvertible {
...
}
The where T : struct... constrains it to enum types only (any any other struct implementing both interfaces, which is probably very low)