In C# what is the best practice for encapsulating variables I need to use in multiple methods? Is it OK to simply declare them at the top of my class above the two methods?
Also if I am using app settings from my config file should I use a getter? like this...
private string mySetting{ get { return WebConfigurationManager.AppSettings["mySetting"]; } }
What best practice is?
2 Answers 2
It's not just OK. According to the book Clean Code it's actually a very good practice, and Uncle Bob really encourages it. A variable used by many methods could show a high degree of cohesion between the methods . Moreover, a high degree of object variables could also hint that said class should be split in two so declaring them as object variables could help you find out hidden class candidates.
Object level variables aren't global variables, so don't be afraid to use them if they should be shared by various methods.
-
thanks for your help, though I think when you said cohesion you really meant coupling.user1944367– user19443672013年04月24日 19:37:18 +00:00Commented Apr 24, 2013 at 19:37
-
No, I meant cohesion. On Software Engineering class also I had a hard time understanding the desire for high cohesion. Usually we lust after low coupling and high cohesion. Coupling is a physical thing that we can see on our own methods. If a class uses another class, then it's coupled to it. If it actually instantiates and object of said class, then it's very couple to it. However, cohesion is more of a logical thing. High cohesion in a class means that its methods belong to a very similar domain, even if they don't share any variable between them.Uri– Uri2013年04月25日 02:31:20 +00:00Commented Apr 25, 2013 at 2:31
-
Various methods using an object variable don't necessarily mean that they are coupled together. I could have a Encrypter class with a char[] password variable, and have Encrypt(string text); and Decrypt(string text); methods inside it. Both of them use the same password variable, but there's no apparent coupling between them. You may notice, however, that they deal with the same domain, and that is, text encryption. As far as I know, they have a high degree of cohesion, although said class could be split in two. One could argue that encryption doesn't belong to the domain of decryption.Uri– Uri2013年04月25日 02:37:56 +00:00Commented Apr 25, 2013 at 2:37
Encapsulating your settings in a constant manner is a great idea.
What I do is create a settings class either one static global one or multiple instance classes which I'll then manage with dependency injection. Then I load all the settings from configuration into that class on start up.
I've also written a little library that makes use of reflection to make this even easier.
Once my settings are in my config file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="Domain" value="example.com" />
<add key="PagingSize" value="30" />
<add key="Invalid.C#.Identifier" value="test" />
</appSettings>
</configuration>
I make a static or instance class depending on my needs. For simple applications with only a few settings one static class is fine.
private static class Settings
{
public string Domain { get; set; }
public int PagingSize { get; set; }
[Named("Invalid.C#.Identifier")]
public string ICID { get; set; }
}
Then using my library call either Inflate.Static
or Inflate.Instance
and the cool thing is I can use any key value source.
using Fire.Configuration;
Inflate.Static( typeof(Settings), x => ConfigurationManager.AppSettings[x] );
All the code for this is in GitHub at https://github.com/Enexure/Enexure.Fire.Configuration
There is even a nuget package:
PM> Install-Package Enexure.Fire.Configuration
Code for reference:
using System;
using System.Linq;
using System.Reflection;
using Fire.Extensions;
namespace Fire.Configuration
{
public static class Inflate
{
public static void Static( Type type, Func<string, string> dictionary )
{
Fill( null, type, dictionary );
}
public static void Instance( object instance, Func<string, string> dictionary )
{
Fill( instance, instance.GetType(), dictionary );
}
private static void Fill( object instance, Type type, Func<string, string> dictionary )
{
PropertyInfo[] properties;
if (instance == null) {
// Static
properties = type.GetProperties( BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly );
} else {
// Instance
properties = type.GetProperties( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly );
}
// Get app settings and convert
foreach (PropertyInfo property in properties) {
var attributes = property.GetCustomAttributes( true );
if (!attributes.Any( x => x is Ignore )) {
var named = attributes.FirstOrDefault( x => x is Named ) as Named;
var value = dictionary((named != null)? named.Name : property.Name);
object result;
if (ExtendConversion.ConvertTo(value, property.PropertyType, out result)) {
property.SetValue( instance, result, null );
}
}
}
}
}
}
WebConfigurationManager.AppSettings
because it's much easer to change later