How to use generics with classes full of constants? - c#

I hope it becomes clear what I mean. I have multiple static class full of options:
static class Thing1
{
public const string Name = "Thing 1";
// More const fields here.
}
static class Thing2
{
public const string Name = "Thing 2";
// More const fields here.
}
Now I want to use those options to create a class which includes the contents of one of these classes.
public void Create<T>()
{
var foo = new Foo(T.Name);
foo.Prop = T.Something;
if (T.HasValue)
foo.Add(T.Value);
}
But of course this doesn't work. I would use interfaces, but static classes can't implement interfaces.
Is there any way to make this work elegantly? Making Thing1 and Thing2 singletons would work, but that isn't a very nice solution.
I could create a struct and put the objects into another static class, but I was wondering whether you could do something like the above.

Well, you can create an interface and make your classes non-static and inherit from this interface:
public class Thing1 : IThing
{
public string Name { get; } = "Thing 1";
// More const fields here.
}
public class Thing2 : IThing
{
public string Name { get; } = "Thing 2";
// More fields here.
}
interface IThing
{
string Name { get; }
}
And then use it for your method together with type parameter constraint:
public void Create<T>(T t) where T : IThing
{
// Now compiler knows that `T` has all properties from `IThing`
var foo = new Foo(t.Name);
foo.Prop = t.Something;
if (t.HasValue)
foo.Add(t.Value);
}

You can try Reflection: scan assemblies for static classes, obtain public const string fields with their values from them and materialize them as a Dictionary<T>
using System.Linq;
using System.Reflection;
...
// Key : type + field name, say Tuple.Create(typeof(Thing1), "Name")
// Value : corresponding value, say "Thing 1";
static Dictionary<Tuple<Type, string>, string> s_Dictionary = AppDomain
.CurrentDomain
.GetAssemblies() // I've taken all assemblies; you may want to add Where here
.SelectMany(asm => asm.GetTypes())
.Where(t => t.IsAbstract && t.IsSealed) // All static types, you may want to add Where
.SelectMany(t => t
.GetFields() // All constant string fields
.Where(f => f.FieldType == typeof(string))
.Where(f => f.IsPublic && f.IsStatic)
.Where(f => f.IsLiteral && !f.IsInitOnly) // constants only
.Select(f => new {
key = Tuple.Create(t, f.Name),
value = f.GetValue(null)
}))
.ToDictionary(item => item.key, item => item.value?.ToString());
If you want to scan not all loaded but just one (executing) assembly
static Dictionary<Tuple<Type, string>, string> s_Dictionary = Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(t => t.IsAbstract && t.IsSealed)
...
Now you can wrap the dictionary, say
public static string ReadConstant<T>(string name = null) {
if (string.IsNullOrEmpty(name))
name = "Name";
if (s_Dictionary.TryGetValue(Tuple.Create(typeof(T), name), out string value))
return value;
else
return null; // Or throw exception
}
Usage
string name1 = ReadConstant<Thing1>();

You could use non static classes and add those to a dictionary having a Type key. But you would have to use read-only properties.
public interface IConstants
{
string Name { get; }
double InitialHealth { get; }
public int? MaxTries { get; }
}
public class Thing1 : IConstants
{
public string Name => "Thing 1";
public double InitialHealth => 100.0;
public int? MaxTries => null;
}
public class Thing2 : IConstants
{
public string Name => "Thing 2";
public double InitialHealth => 80.0;
public int? MaxTries => 10;
}
Initialize the dictionary:
public static readonly Dictionary<Type, IConstants> Constants =
new Dictionary<Type, IConstants> {
[typeof(Thing1)] = new Thing1(),
[typeof(Thing2)] = new Thing2(),
};
The Create function:
public void Create<T>()
{
Type key = typeof(T);
var foo = new Foo(key.Name);
IConstants constants = Constants[key];
foo.InitialHealth = constants.InitialHealth;
if (constants.MaxTries is int maxTries) { // Only true if MaxTries.HasValue.
// Converts to int at the same time.
foo.Add(maxTries);
}
}

Instead of a static classes with constants. You can create a class with properties and static instances with the desired values.
public class Thing
{
private Thing(string name, string something, bool hasValue, string value)
{
Name = name;
Something = something;
HasValue = hasValue;
Value = value;
}
public string Name { get; }
public string Something{ get; }
public bool HasValue { get; }
public string Value{ get; }
public static Thing Thing1 { get; } = new Thing("Thing1", "Something1", true, "Value1");
public static Thing Thing2 { get; } = new Thing("Thing2", "Something2", false, null);
}
And then your method would just take that class.
public void Create(Thing t)
{
var foo = new Foo(t.Name);
foo.Prop = t.Something;
if (t.HasValue)
foo.Add(t.Value);
}
Then you'd call it with either
Create(Thing.Thing1);
or
Create(Thing.Thing2);

After some reflection I came up with another solution. Why select the constants by type? It is much easier if we use the same type to store the different sets of constants.
public class Constants
{
public string Name { get; set; }
public double Health { get; set; }
public int? MaxTries { get; set; }
}
We then identify the sets through an enum:
public enum SetType
{
Set1, // Please use speaking names in a real implementation!
Set2,
Set3
}
We define the values of the constants while creating the dictionary of constants sets:
public static readonly Dictionary<SetType, Constants> ConstantSets =
new Dictionary<SetType, Constants> {
[SetType.Set1] = new Constants { Name = "Set 1", Health = 100, MaxTries = null },
[SetType.Set2] = new Constants { Name = "Set 2", Health = 80, MaxTries = 5 },
...
};
The Create method becomes
public void Create(SetType set)
{
var constants = ConstantSets[set];
var foo = new Foo(constants.Name) {
Health = constants.Health
};
if (constants.MaxTries is int maxTries) {
foo.Add(maxTries);
}
}
No generics, no reflection, no fancy stuff required.

Related

passing around values to an AutoMapper Type Converter from outside

I have a multilingual database, which returns values based on a key and an enum Language. When I convert a DB object to a model, I want the model to contain the translated value based on the key and the current language.
The key comes from the DB object but how can I pass the current language to the the Mapper.Map() function?
Currently, I am using a [ThreadStatic] attribute to set the culture before calling Mapper.Map<>, and to retrieve it in the TypeConverter.
public enum Language
{
English, French, Italian, Maltese
}
public class MultilingualValue<T>
{
public Dictionary<Language, T> Value { get; set; }
public MultilingualValue()
{
this.Value = new Dictionary<Language, T>();
}
}
public class PersonData
{
public string FirstName { get; set; }
public MultilingualValue<string> City { get; set; }
}
public void MapPerson()
{
PersonData personData = new PersonData();
personData.FirstName = "John";
personData.City = new MultilingualValue<string>();
personData.City.Value[ Language.English] = "The Zurrieq";
personData.City.Value[Language.French] = "Le Zurrieque";
MultilingualValueData.CurrentLanguage = Language.English;
var personModel = Mapper.Map<PersonData, PersonModel>(personData);
}
public class MultilingualValueToBasicDataTypeConverter<T> : ITypeConverter<MultilingualValue<T>, T>
{
public T Convert(ResolutionContext context)
{
var currentLanguage = MultilingualValueData.CurrentLanguage; //THIS IS THE [ThreadStatic] VARIABLE
if (currentLanguage == null) throw new InvalidOperationException("Please make sure to fill in CurrentLanguage");
MultilingualValue<T> sourceMultilingualValue = (MultilingualValue < T > )context.SourceValue;
T destinationValue = default(T);
if (sourceMultilingualValue != null)
{
destinationValue = sourceMultilingualValue.Value[currentLanguage.Value];
}
return destinationValue;
}
}
public static class MultilingualValueData
{
[ThreadStatic]
public static Language? CurrentLanguage;
}
I left out the configurations as I think they're unneccessary for this example. If you need them, I'll post them as well.
While this works, I find this workaround quite ugly. Is there any way to pass data through the ResolutionContext?
Just use the Map overload that takes a Action<IMappingOperationOptions>. You can add configuration elements to the Items property that are then passed to your ITypeConverter
public class CustomConverter : ITypeConverter<string, string>
{
public string Convert(ResolutionContext context)
{
return "translated in " + context.Options.Items["language"];
}
}
internal class Program
{
private static void Main(string[] args)
{
AutoMapper.Mapper.CreateMap<string, string>().ConvertUsing<CustomConverter>();
var result = AutoMapper.Mapper.Map<string, string>("value" , opt => opt.Items["language"] = "english");
Console.Write(result); // prints "translated in english"
Console.ReadLine();
}
}

with enum retrieve const value from class c#

i have an enum like
public enum DecimailPrecision
{
One,
Two,
}
and class as
class DecimailPrecision1
{
public const string One = "#,##0.0";
public const string Two = "#,##0.00";
}
i want to retrieve const string from class with enum. i already doing this with if and switch as
string format = string.Empty;
switch (value)
{
case DecimailPrecision.One:
format = DecimailPrecision1.One.ToString(); break;
case DecimailPrecision.Two:
format = DecimailPrecision1.Two.ToString(); break;
default:
format = DecimailPrecision1.Two.ToString(); break;
}
if (value == "One"){
format = DecimailPrecision1.One.ToString();}
else if (value == "Two"){
format = DecimailPrecision1.Two.ToString();}
}
i need a better way because i have lot items in enum.
thanks.
Why not just create a Dictionary<DecimailPrecision, string> and hold the mappings in that?
That way you can simply look up your DecimailPrecision value in the dictionary and retrieve the appropriately mapped string.
You could even store the mapping in config and read it from that, so you wouldn't need to recompile your code to add new mappings.
To explicitly apply this to your code (I'd recommend changing the name of your consts to DecimalPrecisionFormat):
var precisionMap = new Dictionary<DecimailPrecision, string>
{
{ DecimailPrecision.One, DecimailPrecision1.One }
, { DecimailPrecision.Two, DecimailPrecision1.Two }
};
var formatTwo = precisionMap[DecimailPrecision.Two];
For similar needs we developed a custom attribute and a few extension methods.
Usage is like this.
public enum DecimailPrecision
{
[EnumCode("#,##0.0")]
One,
[EnumCode("#,##0.00")]
Two
}
string format = DecimailPrecision.One.GetCode();
It may be meaningless for your case but reverse is valid through like this
string format ="#,##0.00";
DecimailPrecision dp = format.ToEnum<DecimailPrecision>();
Extensions and Atrribute are like:
public static class EnumExtensions
{
private static readonly Dictionary<Type, EnumCodePair[]> EnumCodeCache = new Dictionary<Type, EnumCodePair[]>();
public static string GetCode(this Enum enumValue) {
var codePairs = GetEnumCodePairs(enumValue.GetType());
return codePairs.First(cp => Equals(cp.Enum, enumValue)).Code;
}
public static T ToEnum<T>(this string enumCode) {
var codePairs = GetEnumCodePairs(typeof(T));
return (T)codePairs.First(cp => Equals(cp.Code, enumCode)).Enum;
}
private static IEnumerable<EnumCodePair> GetEnumCodePairs(Type type) {
if(!EnumCodeCache.ContainsKey(type)) {
var enumFields = type.GetFields(BindingFlags.Public | BindingFlags.Static);
var codePairs = new List<EnumCodePair>();
foreach(var enumField in enumFields) {
var enumValue = Enum.Parse(type, enumField.Name);
var codePair = new EnumCodePair {
Enum = enumValue
};
var attrs = enumField.GetCustomAttributes(typeof(EnumCodeAttribute), false);
codePair.Code = attrs.Length == 0
? enumField.Name
: ((EnumCodeAttribute)attrs[0]).Code;
codePairs.Add(codePair);
}
EnumCodeCache.Add(type, codePairs.ToArray());
}
return EnumCodeCache[type];
}
class EnumCodePair
{
public object Enum { get; set; }
public string Code { get; set; }
}
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class EnumCodeAttribute : Attribute
{
public EnumCodeAttribute(string code) {
Code = code;
}
public string Code { get; private set; }
}

How can I set a default value inside of a class as a property of another class?

I need to set a property inside of a class from another class that defines the first class as a property. I want to default a value inside the child class. An example of this would be:
public enum NamingConvention
{
Name1 = 1,
Name2
}
public class Class1
{
public Class1()
{
}
public int Id { get; set; }
public NamingConvention Naming{ get; set; }
}
public class Class2
{
public Class2()
{
}
public List<Class1> Name1s { get; set; }
}
public class Class3
{
public Class2()
{
}
public List<Class1> Name2s { get; set; }
}
I want to be able to put an attribute or something over the Class1 property inside of Class2 and Class3 so that in Class2, the Naming Property gets set to Name1 and and for Class3, it would be automatically set to Name2.
Hope that makes sense. I tried to make this as simple an example as possible. Any ideas out there? I am trying to avoid abstract classes because my real entities are tied to nHibernate and don't want to change the model at this time.
This can be accomplished with the use of the DefaultValueAttribute, a custom TypeConverter and Reflection. It seems unlikely this will perform better than what you are currently doing, but I'll leave that for you to evaluate.
Apply the TypeConverter attribute to Class 1
[TypeConverter(typeof(Class1Converter))]
public class Class1
{
public int Id { get; set; }
public NamingConvention Naming { get; set; }
}
public enum NamingConvention
{
Name1 = 1,
Name2,
Name3,
Name4
}
Define the Class1Converter. Note this simple converter only sets the value of the NamingConvention parameter.
public class Class1Converter: TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override bool CanConvertTo(ITypeDescriptorContext context,
Type destinationType)
{
if(destinationType == typeof(Class1))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertFrom(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture,
object value)
{
var stringValue = value as string;
if(stringValue != null)
{
return new Class1
{
Naming = (NamingConvention)Enum.Parse(typeof(NamingConvention), stringValue)
};
}
return base.ConvertFrom(context, culture, value);
}
}
For convenience I am declaring this in an Extension Method, it could easily be set up as part of the classes with defaults...
public static class DefaultExtension
{
public static IEnumerable<PropertyInfo> GetProperties<T>(this Type type)
{
return type.GetProperties().Where(p => p.PropertyType == typeof (T));
}
public static void SetDefaults<T>(this T toDefault)
{
foreach (PropertyInfo p in toDefault.GetType().GetProperties())
{
foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
{
p.SetValue(toDefault, dv.Value, null);
}
}
}
}
Finally you declare place DefaultValue attributes on your properties. I am calling SetDefaults() from the constructors here for convenience again, in your case you would still need to call it after the instances are loaded from NHibernate.
public class Class2
{
public int X { get; set; }
[DefaultValue(typeof(Class1), "Name2")]
public Class1 Name2Class { get; set; }
public Class2()
{
this.SetDefaults();
}
}
public class Class3
{
public int Y { get; set; }
[DefaultValue(typeof(Class1), "Name3")]
public Class1 Name3Class { get; set; }
public Class3()
{
this.SetDefaults();
}
}
Unit test demonstrating validity...
[Test]
public void TestDefaultValueAttribute()
{
//Class2 have Name2 as the default value for the Naming property
var c2 = new Class2();
Assert.That(c2,Is.Not.Null);
Assert.That(c2.Name2Class, Is.Not.Null);
Assert.That(c2.Name2Class.Naming, Is.EqualTo(NamingConvention.Name2));
//Class3 have Name3 as the default value for the Naming Property
var c3 = new Class3();
Assert.That(c3, Is.Not.Null);
Assert.That(c3.Name3Class, Is.Not.Null);
Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
//wipes out other properties of the Class1 attribute.
// to demonstrate, set properties to something other than the default then call
// SetDefaults again.
c3.Name3Class.Naming = NamingConvention.Name1;
c3.Name3Class.Id = 10;
c3.SetDefaults();
Assert.That(c3.Name3Class.Id, Is.EqualTo(0));
Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
}
You will notice that this wipes out the Id property of Class1 If this is not desired, you could come up with a more targeted version of SetDefaults that only overwrote specific properties of Class1. At this point I don't know if I would really continue using DefaultValue, as use case deviates from the original and using this in combination with the above method would produce unexpected results. I would probably write a custom 'DefaultNaminingConventionAttribute for this purpose.
public static void SetDefaultNamingConvention<T>(this T toDefault)
{
foreach (PropertyInfo p in toDefault.GetType().GetProperties<Class1>())
{
foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
{
var pValue = p.GetValue(toDefault, null) as Class1;
if (pValue != null)
{
pValue.Naming = ((Class1)dv.Value).Naming;
}
else
{
p.SetValue(toDefault, dv.Value, null);
}
}
}
}
[Test]
public void SetDefaultNamingConventionDefaultShouldOnlyDefaultNamingProperty()
{
var c3 = new Class3();
c3.Name3Class.Naming = NamingConvention.Name1;
c3.Name3Class.Id = 20;
c3.SetDefaultNamingConvention();
Assert.That(c3.Name3Class.Id, Is.EqualTo(20));
Assert.That(c3.Name3Class.Naming, Is.EqualTo(NamingConvention.Name3));
}
EDIT: Updated to deal with setting defaults for list members
With this new SetListDefaults extension method, we now can apply the default to members of List<Class1>. Here I would almost definitely no longer use DefaultValue, but would define a custom attribute for use with collections. This is beyond the scope of the question though.
public static class DefaultExtension
{
public static IEnumerable<PropertyInfo> GetProperties<T>(this Type type)
{
return type.GetProperties().Where(p => p.PropertyType == typeof (T));
}
public static void SetListDefaults<T>(this T toDefault)
{
foreach (PropertyInfo p in toDefault.GetType().GetProperties<List<Class1>>())
{
foreach (var dv in p.GetCustomAttributes(true).OfType<DefaultValueAttribute>())
{
var pValue = p.GetValue(toDefault, null) as List<Class1>;
if (pValue != null)
{
foreach (var class1 in pValue)
{
class1.Naming = ((Class1) dv.Value).Naming;
}
}
}
}
}
}
Now provided a class with a List property...
public class Class4
{
public int Z { get; set; }
[DefaultValue(typeof (Class1), "Name4")]
public List<Class1> Name4Classes { get; set; }
}
And a unit test to verify only the Naming Property of each item in the list is modified.
[Test]
public void SetListDefaultsShouldResetNamingConventionOfEachListMember()
{
var c4 = new Class4
{
Z = 100,
Name4Classes = new List<Class1>
{
new Class1 {Id = 1, Naming = NamingConvention.Name1},
new Class1 {Id = 2, Naming = NamingConvention.Name2},
new Class1 {Id = 3, Naming = NamingConvention.Name3}
}
};
Assert.That(c4.Name4Classes, Is.Not.Empty);
Assert.That(c4.Name4Classes.Count, Is.EqualTo(3));
Assert.That(c4.Name4Classes.Any(c => c.Id == 0), Is.False);
Assert.That(c4.Name4Classes.Any(c => c.Naming == NamingConvention.Name4), Is.False);
c4.SetListDefaults();
Assert.That(c4.Name4Classes, Is.Not.Empty);
Assert.That(c4.Name4Classes.Count, Is.EqualTo(3));
Assert.That(c4.Name4Classes.Any(c=> c.Id == 0), Is.False);
Assert.That(c4.Name4Classes.All(c=> c.Naming == NamingConvention.Name4), Is.True);
}
I would use the constructors.
In Class2's constructor:
public Class2()
{
Name1Class = new Class1()
Name1Class.Naming = NamingConvention.Name1
}
In Class3's Constructor:
public Class3()
{
Name2Class = new Class1()
Name2Class.Naming = NamingConvention.Name2
}
If you want to get fancy you could put a parameter on the constructor in Class1 to allow you to set Naming when the object is created.

Update a property on an object knowing only its name

I have some public variables that are defined as follows:
public class FieldsToMonitor
{
public int Id { get; set; }
public string Title { get; set; }
public int Rev {get; set;}
}
I now want to populate those variable with values, but the fm.[varible name] needs be same as field.Name. Here how I would populate in loop if I knew the property name ahead of time and the order of the property name:
// loop 1
fm.Id = revision.Fields[field.Name].Value;
// ... loop 2 ...
fm.Title = revision.Fields[field.Name].Value;
// ... loop 3 ...
fm.Rev = revision.Fields[field.Name].Value;
Here is what I would like to do where field.Name can be substituted with property name:
fm.ID becomes
fm.[field.Name] where field.Name == "ID"
fm.Title becomes
fm.[field.Name] where field.Name == "Title"
fm.Rev becomes
fm.[field.Name] and where field.Name == "Rev"
Is there a solution for this?
Here is more code of what I have so far:
public class FieldsToMonitor
{
public int Id { get; set; }
public string Title { get; set; }
public int Rev {get; set;}
}
static BindingList<FieldsToMonitor> FieldsToMonitorList
= new BindingList<FieldsToMonitor>();
// ...
// Loop through the work item revisions
foreach (Revision revision in wi.Revisions)
{
fm = new FieldsToMonitor();
// Get values for the work item fields for each revision
var row = dataTable.NewRow();
foreach (Field field in wi.Fields)
{
fieldNameType = field.Name;
switch (fieldNameType)
{
case "ID":
case "Title":
case "Rev":
// the following doesn't work
fm + ".[field.Name]" = revision.Fields[field.Name].Value;
fm[field.Name] = revision.Fields[field.Name].Value;
row[field.Name] = revision.Fields[field.Name].Value;
break;
}
}
}
This is all heavily dependent on how these values are being retrieved, and it is difficult to tell from your limited example if the values are strings or the correct type just boxed as an object.
That being said, the following could work (but is hardly efficient):
public static void SetValue<T>(T obj, string propertyName, object value)
{
// these should be cached if possible
Type type = typeof(T);
PropertyInfo pi = type.GetProperty(propertyName);
pi.SetValue(obj, Convert.ChangeType(value, pi.PropertyType), null);
}
Used like:
SetValue(fm, field.Name, revision.Fields[field.Name].Value);
// or SetValue<FieldsToMonitor>(fm, ...);
I am not sure I understand you correctly but here is a try
public static class MyExtensions
{
public static void SetProperty(this object obj, string propName, object value)
{
obj.GetType().GetProperty(propName).SetValue(obj, value, null);
}
}
Usage like
Form f = new Form();
f.SetProperty("Text", "Form222");
You're going to have to use reflection to accomplish that. Here's a short example:
class Foo
{
public int Num { get; set; }
}
static void Main(string[] args)
{
Foo foo = new Foo() { Num = 7 };
Console.WriteLine(typeof(Foo).GetProperty("Num").GetValue(foo, null));
}

Checking a field of a generic type

public static int GetResult<TType>(TType aObject) {
if(aObject.mValue==12)
return 99;
return 20;
}
How can I check the field mValue of TType, I'm guessing reflection may come into this, but I'm unsure how?
Thanks.
Generics are useful when you want to preserve strong typing and compile-time safety. If you are going to resort to Reflection no need to use generics. So one way would be to define an interface or a base class containing this property:
public interface IFoo
{
int Value { get; set; }
}
and then have a generic constraint on the type:
public static int GetResult<TType>(TType aObject) where TType: IFoo
{
if(aObject.Value == 12)
{
return 99;
}
return 20;
}
Here's a pattern that I use:
First create an interface:
IFoo
{
int mValue {get; }
}
Then an "adhoc" class that implements the interface
AdHocIFoo : IFoo
{
Func<int> get_mValue;
public AdHocIFoo(Func<int> getValue)
{
this.get_mValue = getValue;
}
public int mValue { get { return get_mValue(); } }
}
Now, if you have types, say, Bar and Person defined like this:
class Bar
{
public int Baz { get; set; }
}
class Person
{
public int ID {get; set; }
}
Then you can use code similar to the following;
var bar = new Bar() { Baz = 3 };
var per = new Person() { ID = 43 };
var foo1 = new AdHocIFoo(x => bar.Baz);
var foo2 = new AdHocIFoo(x => per.ID);
var result1 = GetResult<AdHocIFoo>(foo1);
var result2 = GetResult<AdHocIFoo>(foo2);
You'd need to restrict TType using the 'where' keyword to a type or interface which you know has a mValue field.
If you don't want to do that, you can use the dynamic keyword
e.g.
dynamic value= aObject
if (value.mValue == 12)
return 99;
return 20;
But this should be a last resort as it will fail at runtime if your object doesn't have a mValue.

Categories

Resources