Reputation: 67195
I'm creating two separate classes for accessing a custom file. Let's call my classes MyReader
and MyWriter
.
I'm following the same pattern that .NET classes such as StreamReader
and StreamWriter
follow, where there is a separate class for reading and writing. In addition to following an established pattern, this also solved some other problems for me.
However, I really need a few common settings for both the reader and writer classes. For example, I have a delimiter character that the user should be able to change. I also have some fixed data that contains common information used for parsing.
Could someone suggest a good pattern, especially one that follows common .NET framework practices, for creating common settings for multiple classes like this? My classes already inherit from a .NET class. Seems like multiple inheritance might have been one way but doesn't seem supported in C#.
(I'm using Visual Studio 2012.)
Upvotes: 4
Views: 1853
Reputation: 2261
public interface IStreamFilter
{
string Delimiter {get; private set;}
List<string> FilterCriteria {get; private set;}
}
public class StreamFilter : IStreamFilter
{
public string Delimiter {get;}
public List<string> FilterCriteria {get;}
public void StreamFilter (string delimiter, List<string> filterCriteria)
{
this.Delimiter = delimiter;
this.FilterCriteria = filterCriteria;
}
}
You can pass an instance of IStreamFilter in the constructor of your Reader and Writer Class.
public class MyReader
{
private IStreamFilter _streamFilter;
public MyReader(IStreamFilter streamFilter)
{
this._streamFilter = streamFilter;
}
public string ReadString()
{
var readString = reader.GetString(x => x.Contains(this._streamFilter.Delimiter);
// apply the filter for reading string
}
}
Anywhere in your code where you want to instantiate MyReader class, you can create a new instance of IStreamFilter, and set the delimiter and other filter criteria as per user preference (let's say from user profile), in the constructor. Then you pass that instance of StreamFilter to your MyReader instance. That way, you can customise the filter settings on the fly without relying on Singletons.
Upvotes: 3
Reputation: 20157
This seems to be one of those rare instances where a Singleton might make good sense:
public abstract class MyBase
{
// This is the .NET class you're inheriting from, just putting it as a placeholder.
}
public class MyReader : MyBase
{
private static readonly IMySettings settings = MySettings.Instance;
}
public class MyWriter : MyBase
{
private static readonly IMySettings settings = MySettings.Instance;
}
internal interface IMySettings
{
// Define your setting's properties, such as delimiter character.
}
internal sealed class MySettings : IMySettings
{
private MySettings()
{
}
public static IMySettings Instance
{
get
{
return Nested.Instance;
}
}
// Implement your setting's properties, such as delimiter character.
private static class Nested
{
private static readonly IMySettings instance = new MySettings();
// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
static Nested()
{
}
public static IMySettings Instance
{
get
{
return instance;
}
}
}
}
Hope this helps.
Upvotes: 1
Reputation: 5241
You could use constructor-based dependency injection to insert a settings class into the classes. It would be simple enough to get an IoC container to spin up a settings class, singleton or not, and inject it based on base class or interfaces.
Upvotes: 1
Reputation: 1827
Try using interfaces. interface
works like abstract class
but can only contain method or property definitions that are to be "fleshed out" by the classes that implement them. Classes can then implement as many interfaces as you need. Take a look at how IEnumerable
allows lists, dictionaries, and arrays to all share a common set of methods, allowing them to be used in foreach
, or how IDisposable
allows a class to be declared in using
.
I made a set of ink textbox controls that are all supposed to get their display style settings from the user config of multiple applications, so I made them grab those settings from an IInkInputSettings interface and implemented that interface in all of the applications' UserConfig classes.
Alternatively, you could achieve global configuration by way of a Singleton:
public class GlobalConfig{
/* your configuration properties here */
private static GlobalConfig instance = null;
public static GlobalConfig Instance{
get{
if(instance == null) instance=new GlobalConfig();
return instance;
}
}
private GlobalConfig(){ /* set default property values */ }
}
This is better than a static class with static members, because it is only instantiated the first time you use it.
Upvotes: 1