C# Dictionary with Named/Labeled Types - c#

I've searched just about everywhere and not even sure it's possible, but what the hey, I thought I would see what you C# wizards might have for a solution or workaround.
TL;DR:
I have a multi-dimensional collection using C# dictionaries and want to indicate what each string in the dictionary is for, something like this:
private Dictionary<string: Area, Dictionary<string: Controller, string: Action>> ActionCollection;
Which of-course does not work. For now I'm just commenting the dictionary.
Suggestions, thoughts, ideas?

You cannot do that, but you could add a summary.
For example:
/// <summary>
/// Dictionary<Area, Dictionary<Controller, Action>>
/// </summary>
private Dictionary<string, Dictionary<string, string>> ActionCollection;
These comments will show up in the intellisense.
Or:
If you want to extract info with reflection, you could use custom attributes
If it is just for readability, you could create aliases for it:
using Area = System.String;
using Controller = System.String;
using Action = System.String;
namespace MyApp
{
public class MyClass
{
private Dictionary<Area, Dictionary<Controller, Action>> ActionCollection;
}
}
But intellisense will show string
#MMM says about invalid xml, you can do this:
/// <summary>
/// Dictionary<Area, Dictionary<Controller, Action>>
/// </summary>

Make a class that pairs the key or the value with the annotation:
class AnnotatedVal {
public string Val {get;}
public string Annotation {get;}
public AnnotatedVal(string val, string annotation) {
// Do null checking
Val = val;
Annotation = annotation;
}
public bool Equals(object obj) {
var other = obj as AnnotatedVal;
return other != null && other.Val == Val && other.Annotation == Annotation;
}
public int GetHashCode() {
return 31*Val.GetHashCode() + Annotation.GetHashCode();
}
}
private Dictionary<AnnotatedVal,Dictionary<AnnotatedVal,AnnotatedVal>> ActionCollection;
Now you can use AnnotatedVal in your dictionaries to assure segregation:
ActionCollection.Add(new AnnotatedVal("hello", "Area"), someDictionary);
if (ActionCollection.ContainsKey(new AnnotatedVal("hello", "Area"))) {
Console.WriteLine("Yes");
} else {
Console.WriteLine("No");
}
if (ActionCollection.ContainsKey(new AnnotatedVal("hello", "Controller"))) {
Console.WriteLine("Yes");
} else {
Console.WriteLine("No");
}
The above should produce
Yes
No
because AnnotatedVal("hello", "Area") and AnnotatedVal("hello", "Controller") use different annotations.

You could wrap each string in it's own class. Then the declaration and intellisense will be descriptive:
public class Area
{
public string area { get; set; }
public override string ToString()
{
return area;
}
}
public class Controller
{
public string controller { get; set; }
public override string ToString()
{
return controller;
}
}
public class Action
{
public string action { get; set; }
public override string ToString()
{
return action;
}
}
private Dictionary<Area, Dictionary<Controller, Action>> ActionCollection;

It's possible by using Tuple Field Names within List:
private List<(string Area, List<(string Controller, string Action)>)> ActionCollection;
That's feature from C# 7.0 or from .NET 4.3 by importing System.ValueTuple nuget.

Related

Custom attributes not behaving like data annotations

I am trying to create a custom attribute in console application but it is not working. My custom attribute never gets called. I found a good example here Custom Attribute not being hit
but not happy with its implementation.
I am wondering how data annotations works in MVC. we don't have to call it separately.
Is MVC calling those data annotations attribute behind the scene?
I wish to create custom attribute that I can use it on any class property same like data annotations attribute. But calling it separately like in above link is not what i am looking.
Here is what I have tried:
using System;
namespace AttributePractice
{
[AttributeUsage(AttributeTargets.Property)]
public class CustomMessageAttribute : Attribute
{
public static readonly CustomMessageAttribute Default = new CustomMessageAttribute();
protected string Message { get; set; }
public CustomMessageAttribute() : this(string.Empty)
{
Console.WriteLine("Default message is empty");
}
public CustomMessageAttribute(string message)
{
Message = message;
}
public string MyMessage =>
Message;
public override bool Equals(object obj)
{
if (obj == this)
return true;
if (obj is CustomMessageAttribute customMessageAttribute)
return customMessageAttribute.Message == MyMessage;
return false;
}
public override int GetHashCode()
{
return MyMessage.GetHashCode();
}
public override bool IsDefaultAttribute()
{
return Equals(Default);
}
}
public class Person
{
//This never works
// I am looking to use this attribute anywhere without calling it
// separately , same like data annotations
[CustomMessage("Hello world")]
public string Name { get; set; }
public int Age { get; set; }
public void DisplayPerson()
{
Console.WriteLine(Name);
Console.WriteLine(Age);
}
}
internal static class Program
{
private static void Main(string[] args)
{
var personObj = new Person
{
Name = "Tom",
Age = 28
};
personObj.DisplayPerson();
}
}
}
Can anybody tell me how to make my custom attribute works like data annotation way?
yes, if you need 10 custom attributes, you should create 10 separate.

Conditionally serialize a object in a collection using Json.net

There is tons of info about skipping Properties based on conditionals, but I would like to skip the entire object based on conditions within the object's class. I would like a solution that is contained within the object's class if at all possible. Keep in mind this is a collection of myObj that I am serializing.
public class myObj
{
bool conditional;
ShouldSerialize()
{
return conditional;
}
}
Or
public class myObj
{
[JsonCondition]
public bool conditional{get;}
}
Or even
[JsonCondition(typeof(MyConditionChecker))]
public class myObj
{
public bool conditional{get;}
}
class MyConditionChecker: JsonCondition
{
public override bool CanConvert(object sourceObj)
{
return (sourceObj as myObj).conditional;
}
}
What I got from your comments you would be best served creating your own wrapper around Json that applies the filtering.
public interface IConditionalSerializer
{
bool ShouldBeSerialized();
}
public static class FilteredSerializer
{
public static string SerializeConditional<T>(IEnumerable<T> input)
where T : IConiditionalSerializer
{
return JsonConvert.SerializeObject(input.Where(e => e.ShouldBeSerialized()));
}
}
public class Demo : IConditionalSerializer
{
public bool ShouldBeSerialized() => false;
}
You might also replace the interface with a reflection approach, but keep in mind the performance loss.
public interface IConiditionChecker
{
bool ShouldBeSerialized(object instance);
}
public class ConditionAttribute : Attribute
{
public Type ConditionChecker { get; set; }
}
public static class FilteredSerializer
{
public static string SerializeConditional(IEnumerable<object> input)
{
var matches = (from entry in input
let att = entry.GetType().GetCustomAttribute<ConditionAttribute>()
let hasChecker = att != null && att.ConditionChecker != null
let checker = hasChecker ? (IConiditionChecker)Activator.CreateInstance(att.ConditionChecker) : null
where checker.ShouldBeSerialized(entry)
select entry);
return JsonConvert.SerializeObject(matches);
}
}
[Condition(ConditionChecker = typeof(SomeChecker))]
public class Demo
{
}
Edit: Based on your comment you could do this. Only must decide wether to use opt-in or opt-out in the where-statement. It must ether be casted != null && casted.ShouldBeSerialized or what it currently says.
public interface IShouldBeSerialized
{
bool ShouldBeSerialized();
}
public static class FilteredSerializer
{
public static string SerializeConditional(IEnumerable<object> input)
{
var matches = (from entry in input
let casted = entry as IShouldBeSerialized
where casted == null || casted.ShouldBeSerialized()
select entry);
return JsonConvert.SerializeObject(matches);
}
}
public class Demo : IShouldBeSerialized
{
public bool ShouldBeSerialized()
{
return false;
}
}
If you're able to use the JSON.NET serializer, in terms of not serializing specific items within a collection, you could make the main collection non serializable, then add another filtered collection that does serialize.
public class Manager
{
[JsonIgnore]
public Employee[] Employees { get; set; }
[JsonProperty("Employees")]
public Employee[] SerializableEmployees
{
get { return Employees.Where(e => e.Name != "Bob").ToArray(); }
set { Employees = value; }
}
}
Alternatively, you could mark your class with the [JsonConverter] attribute and use a custom converter to check your condition. A similar approach that ignores a class entirely is detailed here.

.Net 4.0 Optimized code for refactoring existing "if" conditions and "is" operator

I have following C# code. It works fine; but the GetDestination() method is cluttered with multiple if conditions by using is operator.
In .Net 4.0 (or greater) what is the best way to avoid these “if” conditions?
EDIT: Role is part of the business model, and the destination is purely an artifact of one particular application using that business model.
CODE
public class Role { }
public class Manager : Role { }
public class Accountant : Role { }
public class Attender : Role { }
public class Cleaner : Role { }
public class Security : Role { }
class Program
{
static string GetDestination(Role x)
{
string destination = #"\Home";
if (x is Manager)
{
destination = #"\ManagerHomeA";
}
if (x is Accountant)
{
destination = #"\AccountantHomeC";
}
if (x is Cleaner)
{
destination = #"\Cleaner";
}
return destination;
}
static void Main(string[] args)
{
string destination = GetDestination(new Accountant());
Console.WriteLine(destination);
Console.ReadLine();
}
}
REFERENCES
Dictionary<T,Delegate> with Delegates of different types: Cleaner, non string method names?
Jon Skeet: Making reflection fly and exploring delegates
if-else vs. switch vs. Dictionary of delegates
Dictionary with delegate or switch?
Expression and delegate in c#
Having virtual property which would be overriden in derived classes should do the trick:
class Role
{
public virtual string Destination { get { return "Home"; } }
}
class Manager : Role
{
public override string Destination { get { return "ManagerHome;"; } }
}
class Accountant : Role
{
public override string Destination { get { return "AccountantHome;"; } }
}
class Attender : Role
{
public override string Destination { get { return "AttenderHome;"; } }
}
class Cleaner : Role
{
public override string Destination { get { return "CleanerHome;"; } }
}
class Security : Role { }
I didn't make the property abstract, to provide default Home value when it's not overriden in derived class.
Usage:
string destination = (new Accountant()).Destination;
Console.WriteLine(destination);
Console.ReadLine();
Here's one option:
private static readonly Dictionary<Type, string> DestinationsByType =
new Dictionary<Type, string>
{
{ typeof(Manager), #"\ManagerHome" },
{ typeof(Accountant), #"\AccountantHome" },
// etc
};
private static string GetDestination(Role x)
{
string destination;
return DestinationsByType.TryGetValue(x.GetType(), out destination)
? destination : #"\Home";
}
Note:
This doesn't cope with null parameters. It's not clear whether or not you actually need it to. You can easily add null handling though.
This doesn't copy with inheritance (e.g. class Foo : Manager); you could do that by going up the inheritance hierarchy if necessary
Here's a version which does deal with both of those points, at the cost of complexity:
private static string GetDestination(Role x)
{
Type type = x == null ? null : x.GetType();
while (type != null)
{
string destination;
if (DestinationsByType.TryGetValue(x.GetType(), out destination))
{
return destination;
}
type = type.BaseType;
}
return #"\Home";
}
EDIT: It would be cleaner if Role itself had a Destination property. This could either be virtual, or provided by the Rolebase class.
However, it could be that the destination is really not something the Role should concern itself with - it could be that Role is part of the business model, and the destination is purely an artifact of one particular application using that business model. In that sort of situation, you shouldn't put it into Role, as that breaks separation of concerns.
Basically, we can't tell which solution is going to be most suitable without knowing more context - as is so often the way in matters of design.
Approach 1 (Selected): Using dynamic keyword to implement multimethods / double dispatch
Approach 2: Use a dictionary to avoid if blocks as mentioned in Jon Skeet’s answer below.
Approach 3: Use a HashList with delegates if there is condition other than equality (For example, if input < 25). Refer how to refactor a set of <= , >= if...else statements into a dictionary or something like that
Apporach 4: Virtual Functions as mentioned in MarcinJuraszek’s answer below.
MultiMethods / Double Dispatch approach using dynamic keyword
Rationale: Here the algorithm changes based on the type. That is, if the input is Accountant, the function to be executed is different than for Manager.
public static class DestinationHelper
{
public static string GetDestinationSepcificImplm(Manager x)
{
return #"\ManagerHome";
}
public static string GetDestinationSepcificImplm(Accountant x)
{
return #"\AccountantHome";
}
public static string GetDestinationSepcificImplm(Cleaner x)
{
return #"\CleanerHome";
}
}
class Program
{
static string GetDestination(Role x)
{
#region Other Common Works
//Do logging
//Other Business Activities
#endregion
string destination = String.Empty;
dynamic inputRole = x;
destination = DestinationHelper.GetDestinationSepcificImplm(inputRole);
return destination;
}
static void Main(string[] args)
{
string destination = GetDestination(new Security());
Console.WriteLine(destination);
Console.WriteLine("....");
Console.ReadLine();
}
}
This is a strongly typed, imperative language so if statements and type checking are going to happen.
Having said that, have you considered a virtual method on Role that can be overridden to provide a destination string?
A further alternative, a lookup table!
Dictionary<Type, string> paths = new Dictionary<TYpe, string>()
{
{ typeof(Manager), #"\ManagerHomeA" }
{ typeof(Accountant), #"\AccountantHomeC" }
{ typeof(Cleaner), "Cleaner" }
}
string path = #"\Home";
if(paths.ContainsKey(x.GetType())
path = paths[x];
One way to do it would be to use a map instead of an if:
//(psuedocode)
private Dictionary<Type, string> RoleMap;
void SomeInitializationCodeThatRunsOnce()
{
RoleMap.Add(typeof(Manager), #"\ManagerHome");
RollMap.Add(typeof(Accountant), #"\AccountantHome");
// ect...
}
string GetDestination(Role x)
{
string destination;
if(!RoleMap.TryGet(x.GetType(), out destination))
destination = #"\Home";
return destination;
}
Further reading: http://www.hanselman.com/blog/BackToBasicsMovingBeyondForIfAndSwitch.aspx
Role should have a virtual function that would return destination:
public virtual string GetDestination()
{
return "Home";
}
And all the classes should override this function and return the correct string. Then in the code you would have:
var role = new Accountant();
string destination = role.GetDestination();
I hope that helps. There may be typos, I am writing from head.
you can either use an interface definition or an abstract method / property
with interface:
public interface IDestinationProvider
{
sting Destination { get; }
}
string GetDestination(Role role)
{
var provider = role as IDestinationProvider;
if (provider != null)
return provider.Destination;
return "Default";
}
with an abstract base class
abstract class Role
{
public abstract string GetDestination();
}
class Manager : Role
{
public virtual string GetDestination() { return "ManagerHomeA"; }
}
string GetDestination(Role role)
{
return #"\" + role.GetDestination();
}
or with attributes:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class DestinationAttribute : Attribute
{
public DestinationAttribute() { this.Path = #"\Home"; }
public string Path { get; set; }
}
[Destination(Path = #"\ManagerHome")]
public class Manager : Role { }
string GetDestination(Role role)
{
var destination = role.GetType().GetCustomAttributes(typeof(DestinationAttribute), true).FirstOrDefault();
if (destination != null)
return destination.Path;
return #"\Home";
}

Difficulties with using static functions and variables in C#

I have a class like this
#region Properties
private static string inputURL;
public static string InputURL
{
get { return inputURL; }
set { inputURL = value; }
}
private static string outputURL;
private static string ffBaseURL = "format=xml&";
public static string FFBaseURL
{
get { return ffBaseURL; }
set { ffBaseURL = value; }
}
private static string excludeParam = "fullurl,log";
public static string ExcludeParam
{
get { return excludeParam; }
set { excludeParam = value; }
}
private static string currentCategoryID = "234";
public static string CurrentCategoryID
{
get { return currentCategoryID; }
set { currentCategoryID = value; }
}
private static string navigationParameters = "query=*&log=navigation&filterCategoryId=" + currentCategoryID;
public static string NavigationParameters
{
get { return navigationParameters; }
set { navigationParameters = value; }
}
#endregion
#region Methods
public static string NavigationCall()
{
List<string> excludeParams = new List<string>(excludeParam.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries));
foreach (string key in HttpContext.Current.Request.QueryString.Keys)
{
if (!excludeParams.Contains(key))
{
FFBaseURL += key + "=" + HttpContext.Current.Request[key] + "&";
}
}
FFBaseURL += NavigationParameters;
if (Common.IsInternalIP())
{
FFBaseURL += "&log=internal";
}
outputURL = ffBaseURL;
return outputURL;
}
#endregion
As you can see I have a static function called NavigationCall() ,it is mandatory that this function remains static.And when I calls this function from my website the function returns wrong values in each function call because of the static properties i declared.We all know static properties will retain their values after the exection of the programe.
So lets say when i call these function first time I gets a result "tesresult1",second time when i reloads my webpage it gives me a result "testresult1testresult1".I think you got the problem now.
I Have tried to solve this issue by declaring static variable values again ,but it does not looks like a good way to programe things.
I tried to make the properties non static .but it returns error as NavigationCall() is a static function i can't call non static properties inside it.
Now I am searching for a correct way to resolve this issue, I think this problem came to me because of the wrong understanding of OOPS concept.Can any one lend a hand here to solve the case or if the issue is vast point to some resources where i can understand how to find a solution?
Instead of using static properties, you can pass all the parameters to your static method.
public static string NavigationCall(
string inputURL,
string ffBaseURL,
string excludeParam,
string currentCategoryID,
string navigationParameters
)
{
// the body of your method
}
You can also bundled all properties into Custom object and pass it to method. Also you have to make NavigationCall thread safe for any solution. Are static methods thread safe ?
public static string NavigationCall(CustomNavigation objCustomNavigation)
//Custom object.
public class CustomNavigation
{
public string InputURL {get;set;}
public string FBaseURL{get;set;}
public string ExcludeParam{get;set;}
public string CurrentCategoryID {get;set;}
public string NavigationParameters{get;set;}
}
I'd suggest to introduce a parameter object (as #mit suggested) and use the opportunity to encapsulate some of your logic there. This should instantly simplify your method. Maybe you could then make some of these properties private, because they'll only be needed in logic encapsulated in the parameter object.
//Your static method
public static string NavigationCall(CustomNavigation customNavigation)
//Disclaimer: I have no idea, whether this is an appropriate name.
//It really depends on what you want to do with his class
class CustomNavigation
{
public string InputURL { get; private set; }
public string FFBaseURL { get; private set; }
public IEnumerable<string> ExcludeParams { get; private set; }
public string CurrentCategoryID { get; private set; }
public string NavigationParameters { get; private set; }
public CustomNavigation(string inputUrl, string excludeParam, string fBaseUrl, string currentCategoryID, string navigationParameters)
{
// various guard clauses here...
NavigationParameters = navigationParameters;
CurrentCategoryID = currentCategoryID;
FFBaseURL = fBaseUrl;
InputURL = inputUrl;
// Parse string here -> Makes your method simpler
ExcludeParams = new List<string>(excludeParam.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries));
}
//Example for encapsulating logic in param object
public void AddKeys(HttpContext currentContext)
{
var keys = currentContext.Request.QueryString.Keys
.Cast<string>()
.Where(key => !ExcludeParams.Contains(key));
foreach (var key in keys)
FFBaseURL += key + "=" + currentContext.Request[key] + "&";
}
}

Keep enum-to-object mapping with enum class?

I frequently need a global hard-coded mapping between an enum and another object (a string in this example). I want to co-locate the enum and mapping definitions to clarify maintenance.
As you can see, in this example, an annoying class with one static field is created.
public enum EmailTemplates
{
// Remember to edit the corresponding mapping singleton!
WelcomeEmail,
ConfirmEmail
}
public class KnownTemplates
{
public static Dictionary<EmailTemplates, string> KnownTemplates;
static KnownTemplates() {
KnownTemplates.Add(EmailTemplates.WelcomeEmail, "File1.htm");
KnownTemplates.Add(EmailTemplates.ConfirmEmail, "File2.htm");
}
}
Sometimes the mapping class can have more function and a meaningful name, and the mapping activity can even be private. But that only pollutes the maintenance/correlation problem.
Anyone have a good pattern for this?
You can use attributes to annotate the enumeration and then use reflection to build the dictionary.
[AttributeUsage(AttributeTargets.Field)]
sealed class TemplateAttribute : Attribute {
public TemplateAttribute(String fileName) {
FileName = fileName;
}
public String FileName { get; set; }
}
enum EmailTemplate {
[Template("File1.htm")]
WelcomeEmail,
[Template("File2.htm")]
ConfirmEmail
}
class KnownTemplates {
static Dictionary<EmailTemplate, String> knownTemplates;
static KnownTemplates() {
knownTemplates = typeof(EmailTemplates)
.GetFields(BindingFlags.Static | BindingFlags.Public)
.Where(fieldInfo => Attribute.IsDefined(fieldInfo, typeof(TemplateAttribute)))
.Select(
fieldInfo => new {
Value = (EmailTemplate) fieldInfo.GetValue(null),
Template = (TemplateAttribute) Attribute
.GetCustomAttribute(fieldInfo, typeof(TemplateAttribute))
}
)
.ToDictionary(x => x.Value, x => x.Template.FileName);
}
}
If you do this a lot you can create a more general generic function that combines enumeration values with an attribute associated with that enumeration value:
static IEnumerable<Tuple<TEnum, TAttribute>> GetEnumAttributes<TEnum, TAttribute>()
where TEnum : struct
where TAttribute : Attribute {
return typeof(TEnum)
.GetFields(BindingFlags.Static | BindingFlags.Public)
.Where(fieldInfo => Attribute.IsDefined(fieldInfo, typeof(TAttribute)))
.Select(
fieldInfo => Tuple.Create(
(TEnum) fieldInfo.GetValue(null),
(TAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof(TAttribute))
)
);
}
And use it like this:
knownTemplates = GetEnumAttributes<EmailTemplate, TemplateAttribute>()
.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2.FileName);
For even more fun you can create an extension method:
static class EmailTemplateExtensions {
static Dictionary<EmailTemplate, String> templates;
static EmailTemplateExtensions() {
templates = GetEnumAttributes<EmailTemplate, TemplateAttribute>()
.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2.FileName);
}
public static String FileName(this EmailTemplate emailTemplate) {
String fileName;
if (templates.TryGetValue(emailTemplate, out fileName))
return fileName;
throw new ArgumentException(
String.Format("No template defined for EmailTemplate.{0}.", emailTemplate)
);
}
}
Then calling EmailTemplate.ConfirmEmail.FileName() will return File2.htm.
Here is an approach which worked pretty well for me.
public class BaseErrWarn : Attribute
{
public string Code { get; set; }
public string Description { get; set; }
public BaseErrWarn(string code, string description)
{
this.Code = code;
this.Description = description;
}
}
public enum ErrorCode
{
[BaseErrWarn("ClientErrMissingOrEmptyField", "Field was missing or empty.")] ClientErrMissingOrEmptyField,
[BaseErrWarn("ClientErrInvalidFieldValue", "Not a valid field value.")] ClientErrInvalidFieldValue,
[BaseErrWarn("ClientErrMissingValue", "No value passed in.")] ClientErrMissingValue
}
Now you can use reflection to map the Enum to the BaseErrWarn class:
public static BaseErrWarn GetAttribute(Enum enumVal)
{
return (BaseErrWarn)Attribute.GetCustomAttribute(ForValue(enumVal), typeof(BaseErrWarn));
}
private static MemberInfo ForValue(Enum errorEnum)
{
return typeof(BaseErrWarn).GetField(Enum.GetName(typeof(BaseErrWarn), errorEnum));
}
Here is an example which uses this mapping to map an Enum to an object and then pull fields off of it:
public BaseError(Enum errorCode)
{
BaseErrWarn baseError = GetAttribute(errorCode);
this.Code = baseError.Code;
this.Description = baseError.Description;
}
public BaseError(Enum errorCode, string fieldName)
{
BaseErrWarn baseError = GetAttribute(errorCode);
this.Code = baseError.Code;
this.Description = baseError.Description;
this.FieldName = fieldName;
}
Normally, when you want to add extra info or behaviors to your enum elements, that means you need a full blown class instead. You can borrow from (old-)Java the type-safe enum pattern and create something like this:
sealed class EmailTemplate {
public static readonly EmailTemplate Welcome = new EmailTemplate("File1.html");
public static readonly EmailTemplate Confirm = new EmailTemplate("File2.html");
private EmailTemplate(string location) {
Location = location;
}
public string Location { get; private set; }
public string Render(Model data) { ... }
}
Now you can associate any properties or methods to your elements, like Location and Render above.

Categories

Resources