Protobuf-net serialize enum with value out of range - c#

C# allows to assign any integer value to enum.
When I try to serialize (via protobuf-net) object with enum field which value is out of range, it throws exception: No wire-value is mapped to the enum PersonLevel.
My enum PersonLevel doesn't have Flags attribute.
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public enum PersonLevel
{
Unknown = 1
}
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class Person
{
...
public PersonLevel PersonLevel { get; set; }
...
}
var ms = new MemoryStream();
var person = new Person
{
...
PersonLevel = (PersonLevel) 500
...
};
Serializer.Serialize(ms, person); //No wire-value is mapped to the enum PersonLevel
Is there any facilities to do it without changing business objects (maybe any protobuf attrubutes)?

There are a couple of ways of telling it to simplify the rules; as Ravadre notes, [Flags] automatically disables validation - it causes EnumPassthru to become toggled. You can also do this manually - as long as it is before you start serializing / deserializing:
RuntimeTypeModel.Default[typeof(PersonLevel)].EnumPassthru = true;
which has the description:
/// <summary>
/// Gets or sets a value indicating that an enum should be treated directly as an int/short/etc, rather
/// than enforcing .proto enum rules. This is useful *in particular* for [Flags] enums.
/// </summary>

What you can do is create an int field which you will pack into protobuf message and expose a Property, which will expose your int field as an enum of your type (being a wrapper).
This might be harder if you are using implicit fields, because probably protobuf will try to serialize both, your integer and your enum property. You can try to explicitly [ProtoIgnore] your enum property.
However, protobuf does this automatically for you if your enum is marked with [Flags] attribute, so changing your enum to:
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
[Flags]
public enum PersonLevel
{
Unknown = 1
}
should make it work. At least in version 2.

Related

protobuf-net : enum type conversion error

namespace MyNamespace
{
public enum MyEnum
{
EnumName1 = 1,
EnumName2 = 2,
...
[ProtoContract(Name=#"MyClassProto")]
[Serializable]
public class MyClass : IExtensible
{
[ProtoMember(1, IsRequired = false, Name = #"MyEnumProperty", DataFormat = ProtoBuf.DataFormat.Default)]
[System.ComponentModel.DefaultValue(1)]
public MyEnum MyEnumProperty;
...
var myObjectIn = new MyClass
{
MyEnumProperty = MyEnum.EnumName1,
...
};
MyClass myObjectOut;
using (var stream = new MemoryStream())
{
ProtoBuf.Serializer.Serialize(stream, myObjectIn);
stream.Seek(0, SeekOrigin.Begin);
myObjectOut = ProtoBuf.Serializer.Deserialize<MyClass>(stream);
}
System.InvalidCastException : Invalid cast from 'System.Int32' to
'MyNamespace.MyEnum'.
I am using protobuf-net v2.1.0, and depending on approach I get two distinct ProtoExceptions. Either No wire-value is mapped to the enum MyEnum, or No parameterless constructor found. Reasons are: the default int value 0 is not a constant in your enum type, or you haven't assigned a default value to it in a parameter constructor.
Workaround (logical or):
public enum MyEnum { EnumName1 = 1, EnumName2 = 2, #default = 0 }
or
public MyClass() { MyEnumProperty = MyEnum.EnumName1 }
From ECMA-334 C# Language Specification
An enum declaration that does not explicitly declare an underlying
type has an underlying type of int. If the enum member is the first enum member declared in the enum type, its associated value is zero.
Enums are used for “multiple choice” scenarios, in which a runtime
decision is made from a fixed number of choices that are known at
compile-time
So, your enum with a runtime value you haven't defined as a compile-time constant, imo, is a bad enum.

Converting enum values to arbitrary integers when using Newtonsoft.Json

I have an enum:
public enum IdleDelayBreakMode
{
Repeat,
ShowNext
}
I'm using NewtonSoft.Json to convert to json objects containing properties of that enum's type. What's the best solution to serialize values of that enum to arbitrary integers? Ideally i would like to do something like on the snippet below and i want to know if maybe there's a built-in solution for that:
public enum IdleDelayBreakMode
{
[JsonValue(100)]
Repeat, // When serializing will be converted to 100
[JsonValue(200)]
ShowNext // When serializing will be converted to 200
}
You can just set enum values like that:
public enum IdleDelayBreakMode
{
Repeat = 100,
ShowNext = 200
}
Newtonsoft.Json will use enum values. There is no need to set attributes. This way it will be consistent across the system whether you need to serialize it to JSON, save it in the database using Entity Framework etc.
Are you using your enum as integer constants storage?
In this case you might want to inherit it from int:
public enum IdleDelayBreakMode : int
Assuming you don't want to modify the underlying enum values (as is shown in the other answers), you can decorate your enum vales with [EnumMember(Value = "Name")] attributes and use the alternate numeric values as the name strings:
[JsonConverter(typeof(StringEnumConverter))]
[DataContract]
public enum IdleDelayBreakMode
{
[EnumMember(Value = "100")]
Repeat,
[EnumMember(Value = "200")]
ShowNext
}
You will also need to serialize using StringEnumConverter, either by adding [JsonConverter(typeof(StringEnumConverter))] directly to the enum or by applying it globally according to this answer.
This works with [Flags] as well. Serializing both values of the following:
[Flags]
[DataContract]
[JsonConverter(typeof(StringEnumConverter))]
public enum IdleDelayBreakModeFlags
{
[EnumMember(Value = "100")]
Repeat = (1 << 0),
[EnumMember(Value = "200")]
ShowNext = (1 << 1),
}
produces "100, 200".
Adding these attributes will cause DataContractSerializer as well as Json.NET to use these alternate name strings. If you would prefer not to affect the behavior of the data contract serializer, remove [DataContract] but keep the [EnumMember] attributes.
Just set the integer values in your enum items directly instead of using an attribute:
public enum IdleDelayBreakMode
{
Repeat = 100,
ShowNext = 200
}
JSON.Net will them use the integer values when serializing/deserializing a property of type IdleDelayBreakMode

Enum value as hidden in C#

I have an enum and i want to "hide" one of its values (as i add it for future support).
The code is written in C#.
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
Reserved
}
I don't want to allow peoples who use this code to use this values (MyEnum.Reserved).
Any idea?
TIA
You could use the 'Obsolete' attribute - semantically incorrect, but it will do what you want:
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
[Obsolete("Do not use this", true)]
Reserved
}
Anyone who tries to compile using the Foo.Reserved item will get an error
If you don't want to show it, then don't include it:
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
}
Note that a user of this enum can still assign any integer value to a variable declared as MyEnum:
MyEnum e = (MyEnum)2; // works!
This means that a method that accepts an enum should always validate this input before using it:
void DoIt(MyEnum e)
{
if (e != MyEnum.ValueA && e != MyEnum.ValueB)
{
throw new ArgumentException();
}
// ...
}
So, just add your value later, when you need it, and modify your methods to accept it then.
This is not possible in C#. All enum values are accessible if the Enum itself is accessible.
The only way you could simulate accomplishing this is by using a less accessible static field that used an integer value not already used in the Enum.
public enum MyEnum {
ValueA = 0;
ValueB = 1;
}
internal static class MyEnumEx {
internal static MyEnum Reserved = (MyEnum)42;
}
I'm curious though as to why you would want to do this. No matter what you do users can still provide the Reserved value. All that needs to be done is to cast an int of the appropriate value to the MyEnum type.
// Code that shouldn't access Reserve
MyEnum reserved = (MyEnum)42; // Woot!
if you want to hide from intellisense or PropertyGrid enumerated members, you can apply:
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
and
[System.ComponentModel.Browsable(false)]
Example:
public enum MyEnum
{
A,
B,
[System.ComponentModel.Browsable(false)]
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
C
}
C is not visible
You can achieve something like this by using your own custom type instead of enum:
// This type works pretty much the same way an enum works;
// each specific value can be cast to/from an int, and each has
// a specific name that is returned on calling ToString().
public sealed class MyEnum
{
private readonly int _value;
private readonly string _name;
// private constructor -- ensure that the static members you define below
// are the only MyEnum instances accessible from any outside code
private MyEnum(int value, string name)
{
_value = value;
_name = name;
}
// no need to override Equals or GetHashCode, believe it or not --
// one instance per value means we can use reference equality and
// that should be just fine
public override string ToString()
{
return _name;
}
// provide direct access only to these members
public static readonly MyEnum ValueA = new MyEnum(0, "ValueA");
public static readonly MyEnum ValueB = new MyEnum(1, "ValueB");
// this member is only available to you within the current assembly
internal static readonly MyEnum Reserved = new MyEnum(-1, "Reserved");
}
You could even further emulate the behavior of enum values by, for example, overloading the explicit operators to convert to/from MyEnum objects to int values (took JaredPar's suggestion to use this rather than implicit):
public static explicit operator MyEnum(int value)
{
switch (value)
{
case 0:
return ValueA;
case 1:
return ValueB;
default:
throw new InvalidCastException();
}
}
public static explicit operator int(MyEnum value)
{
return value._value;
}
You can't hide it, but you can add a comment that this value doesn't have any effect at this moment. Otherwise just remove it and add it if you add the support, this shouldn't break any other code dependent on it, as long as the original values don't change.
Are you using the "hidden" value internally? If not:
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
//TODO: Reserved
}
You gain nothing by defining an unused variable.
one way you can do this set the value of this variable null.
so when ever its called from enum it'll b null. in short user can't access its value.
public enum MyEnum
{
ValueA = 0,
ValueB = 1,
Reserved.None
}
I had a similar problem with enum Flags, but the solution I used should work without the Flags attribute.
You can create two enum types, one for the reserved values and the other for the publicly usable values. You can use HasFlag with the reserved type without casting, but assignment requires casting.
[Flags]
public enum MyEnumReserved {
Income = 1,
Expense = 2,
Discretionary = 4,
Critical = 8
}
[Flags]
public enum MyEnum {
Income = MyEnumReserved.Income,
DiscretionaryExpense = MyEnumReserved.Expense | MyEnumReserved.Discretionary,
CriticalExpense = MyEnumReserved.Expense | MyEnumReserved.Critical
}
bool IsIncome(MyEnum val) => val.HasFlag(MyEnumReserved.Income);
...
MyEnum foo = (MyEnum)MyEnumReserved.Expense | (MyEnum)MyEnumReserved.Discretionary;

Why is a Nullable<T> not a valid Custom Attribute Parameter when T is?

If I have an enum like this
public enum Hungry
{
Somewhat,
Very,
CouldEatMySocks
}
and a custom attribute like this
public class HungerAttribute : Attribute
{
public Hungry HungerLevel { get; set; }
public Hungry? NullableHungerLevel { get; set; }
}
I can do this
[Hunger(HungerLevel = Hungry.CouldEatMySocks)]
public class Thing1
but I can't do this
[Hunger(NullableHungerLevel = Hungry.CouldEatMySocks)]
public class Thing2
It generates an error that says "'NullableHungerLevel' is not a valid named attribute argument because it is not a valid attribute parameter type".
Why is that not allowed? I understand that fundamentally it just isn't on the list of accepted types. The valid types seem to be primitives, enums, string, type, and one dimensional arrays of the preceding types.
Is this just an old rule that did not get updated when Nullable came along?
Hungry? is equal to Nullable<Hungry>, which in terms mean that
[Hunger(NullableHungerLevel = Hungry.CouldEatMySocks)]
is equal to
[Hunger(NullableHungerLevel = new Nullable<Hungry>(Hungry.CouldEatMySocks))]
Since you can only use constant values in named attribute arguments you will have to resort to Shimmy's solution.
To get around this create another initializer in your Attribute:
class Program
{
[Hunger()]
static void Main(string[] args)
{
}
public sealed class HungerAttribute : Attribute
{
public Hungry? HungerLevel { get; }
public bool IsNull => !_HungerLevel.HasValue;
public HungerAttribute()
{
}
//Or:
public HungerAttribute(Hungry level)
{
HungerLevel = level;
}
}
public enum Hungry { Somewhat, Very, CouldEatMySocks }
}
I understand that you're not going to use both properties.
Attributes may have as only parameters primitives, typeof expressions and array-creation expression.
Nullable is a struct.
Therefore it is not allowed there.
I suspect the assembly file format itself doesn't allow storage of complex types like structs in the place where attribute values are stored.
I don't know of any plans to change that. But I cannot explain why this restriction exist.
Instead of creating nullable enum, you can create default value for that enum. Enum pick default from 1st value, so set your enum like this
public enum Hungry
{
None,
Somewhat,
Very,
CouldEatMySocks
}
in your code you could do this to check for null
if(default(Hungry) == HungerLevel)//no value has been set

Enum with default typecast? is that possible?

is it possible to make a default typecast for an enum?
I use enum for a lot, such as states and I want to compare enums directly to LINQ fields, but I have to typecast all the time.
You should be able to have properties in your LINQ objects that have an enum type. This way you do not have to cast.
So just change your properties to have the correct enum type and you don't have to worry about casts any longer. You can do this in the LINQtoSQL designer. Just right-click on a property, select 'Properties' and set the appropriate Type in the Visual Studio Properties window.
The answer was MUCH more simple!!!
A good friend of mine told me this is very simple! have a look at this sample!
public enum State:byte
{
EmailNotValidated = 0x00,
EmailValidated = 0x10,
Admin_AcceptPending = 0x40,
Active = 0x80,
Admin_BlockedAccount = 0xff
}
Pay attention to the :BYTE part after the name of the Enum... there is the trick I was looking for! But thanks to everyone trying for me!
LINQ-to-SQL will usually handle direct integer maps and exact string (name) maps (note: case sensitive). Meaning: write your enum somewhere, and in the designer set the property type as the fully-qualified enum name: Some.Namespace.MyEnum. It should usually work.
For non-trivial mappings (for example where the column is a varchar with mixed-case values, or things like "In Progress" [note the space]), you will have to leave the storage property as int/varchar (etc) and map it manually. I usually do this by marking it as private and naming it FooStorage, and adding a mapping property in a partial class:
partial class MyType {
public MyEnum Foo {
get {... mapping logic reading from FooStorage...}
set {... mapping logic, updating FooStorage...}
}
}
The only problem is that LINQ queries will only work against the storage property (not the bespoke property).
Have you tried extension methods?
public enum MyEnum
{
First = 1,
Second = 2,
Third = 3
}
public static class Utility
{
public static string Description(this Enum e)
{
Type t = e.GetType();
DescriptionAttribute[] desc =
(DescriptionAttribute[])(t.GetField(e.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false));
return desc.Length > 0 ? desc[0].Description : e.ToString();
}
public static byte ToByte(this Enum ai)
{
object o=Enum.ToObject(ai.GetType(), ai);
return Convert.ToByte(o);
}
}
class Program
{
static void Main(string[] args)
{
MyEnum me = MyEnum.Third;
Console.WriteLine("Value: {0}\r\nType: {1}"
,me.ToByte(),me.ToByte().GetType().ToString());
Console.ReadLine();
}
}
It outputs:
Value: 3
Type: System.Byte

Categories

Resources