Generic property-list with any generic argument - c#

I need to instantiate a list-property where the generic type can be anything.
So my Main-method looks like this: (In real, ParsingObject<T> are objects I get from a service)
public static void Main()
{
Parser parser = new Parser();
parser.AddAnObject(
new ParsingObject<int>{PropertyName = "FirstProperty", Active=true, DefaultValue=1}
);
parser.AddAnObject(
new ParsingObject<bool>{PropertyName = "SecondProperty", Active=false, DefaultValue=false}
);
parser.Parse();
}
ParsingObject gets any type (I think only string, bool, int,...) as generic. Now in my parser I need to add this object into a List<ParsingObject<T>> like:
public class Parser
{
private readonly List<ParsingObject<T>> _listOfObjects = new List<ParsingObject<T>>();
public void AddAnObject<T>(ParsingObject<T> item)
{
_listOfObjects.Add(item);
}
public void Parse()
{
foreach(var item in _listOfObjects.Where(w=>Active))
{
DoSomething(item);
}
}
}
but I know, I cannot set T as generic argument when instantiating the list (compiler is crying..).
So I could solve this with using ArrayList - but then I can't access the properties of each object. (See the Parse()-method)
for completeness, here is my ParsingObject<T>-class:
public class ParsingObject<T>
{
public string PropertyName { get; set; }
public bool Active { get; set; }
public T DefaultValue { get; set; }
}
Any idea how I could solve this? I cannot modify the ParsingObject<T>-class.

Depending on what exactly is your end goal, maybe something like this would be sufficient:
public class ParsingObjectBase
{
public string PropertyName { get; set; }
public bool Active { get; set; }
public Type ValueType { get; protected set; }
public object DefVal { get; protected set; }
}
public class ParsingObject<T> : ParsingObjectBase
{
public object DefaultValue
{
get { return (T)DefVal; }
set { DefVal = value; }
}
public ParsingObject()
{
ValueType = typeof(T);
}
}
private readonly List<ParsingObjectBase> _listOfObjects = new List<ParsingObjectBase>();
public void AddAnObject<T>(ParsingObject<T> item)
{
_listOfObjects.Add(item);
}
public void Parse()
{
foreach(var item in _listOfObjects.Where(w=>w.Active))
{
DoSomething(item); //do what exactly?
}
}
You obviously can't do without casting either to concrete ParsingObject<T> or DefVal value in this case, but you have Type information stored in one place and have access to your specific properties. Maybe changing ValueType to some kind of enum would be easier to use with switch?

Related

Using generic types within a foreach loop

I really cannot figure out how to use generic types with IEnumerable so that I can iterate through values contained by a given generic value.
Consider the following class (note that the classes here are only for example purposes):
public class Parameter<T> : IParameter<T> where T : IEnumerable<T>
{
public List<UInt64> output = new List<UInt64>();
private T _value;
public T Value
{
get => ...;
set
{
// I want to be able to apply special treat to the value
// Value can be of any type: int, int[], bool, bool[]
foreach (var v in value)
{
output.Add(Convert.UInt64(v) + 5);
}
...
}
}
}
public interface IParameter<T> where T : IEnumerable<T>
{
T Value { get; set; }
}
I then have a test module that instantiate some parameters as per, but I cannot even compile here. I have even tried to replace bool[] to IEnumerable here below, but the compiler does not like it either.
public class TestModule : ModuleBase, ITestModule
{
public IParameter<bool[]> Test1 { get; set; } = new Parameter<bool[]>();
public IParameter<uint[]> Test2 { get; set; } = new Parameter<uint[]>();
...
public IParameter<int> Test3 { get; set; } = new Parameter<int>();
}
I did consider using overload for the Parameter() class, but I thought it to be overkill to create a class per supported type (considering it is only for the Value property).
Your issue is that your generic parameter is specified incorrectly.
public class Parameter<T> : IParameter<T> where T : IEnumerable<T>
implies that whatever comes in of type T is an enumerable of the same type, meaning for instance a T of type bool[] should be an IEnumerable<bool[]> which is clearly incorrect.
One way to get it to compile is this:
public class Parameter<TEnumerable, TType> : IParameter<TEnumerable, TType> where TEnumerable : IEnumerable<TType>
{
public List<ulong> output = new List<ulong>();
private TEnumerable _value;
public TEnumerable Value
{
get => { return null; }
set
{
// I want to be able to apply special treat to the value
// Value can be of any type: int, int[], bool, bool[]
foreach (Q v in value)
{
output.Add(Convert.ToUInt64(v) + 5);
}
}
}
}
public interface IParameter<TEnumerable, TType> where TEnumerable : IEnumerable<TType>
{
TEnumerable Value { get; set; }
}
public class TestModule
{
public IParameter<bool[], bool> Test1 { get; set; } = new Parameter<bool[], bool>();
public IParameter<uint[], uint> Test2 { get; set; } = new Parameter<uint[], uint>();
public IParameter<int[], int> Test3 { get; set; } = new Parameter<int[], int>();
}
As for your additional comment, no, there's no way you can avoid having to specify the two types since IEnumerable is not a T in the form you've formulated your code. You have 2 separate parameters here and as such, you will have to use 2 generic parameters if you must do it the way you've done it.
A much simpler solution to your problem would be something like this which serves the same purpose more or less, although I don't really know your requirements so this may or may not suffice (interface omitted for clarity):
public class Parameter<TType>
{
public List<ulong> output = new List<ulong>();
private IEnumerable<TType> _value;
public IEnumerable<TType> Value
{
get => { return null; }
set
{
// I want to be able to apply special treat to the value
// Value can be of any type: int, int[], bool, bool[]
foreach (TType v in value)
{
output.Add(Convert.ToUInt64(v) + 5);
}
}
}
}
public class TestModule
{
public Parameter<bool> Test1 { get; set; } = new Parameter<bool>();
public Parameter<uint> Test2 { get; set; } = new Parameter<uint>();
public Parameter<int> Test3 { get; set; } = new Parameter<int>();
}

Auto-properties with mutable objects

I'm trying to make properties for mutable objects. Is this a problem with Auto-properties? For example, the following code would allow for unwanted manipulation of the mutable object. How would I avoid this?
public class Mutable{
public int Value { get; set; }
}
public class ClassWithMutable{
public Mutable Object { get; }
public ClassWithMutable(){
this.mutable = new Mutable();
this.mutable.Value = 0;
}
}
public class Demo{
public static void Main(String[] args){
ClassWithMutable test = new ClassWithMutable();
Mutable o = test.Object;
o.Value = 1;
}
}
You could use an interface that only exposes the get of the properties, and a private class that implements it.
public interface IImmutable {
int Value { get; }
}
public class ClassWithImmutable{
private Mutable _object;
public IImmutable Object { get { return _object; } }
public ClassWithImmutable(){
this._object = new Mutable();
this._object.Value = 0;
}
private class Mutable : IImmutable {
public int Value { get; set; }
}
}
public class Demo{
public static void Main(String[] args){
ClassWithImmutable test = new ClassWithImmutable();
IImmutable o = test.Object;
o.Value = 1; // fails
}
}
I'm trying to understand the intent of your question rather than your question, and I'm coming up a little short. However, I think I came up with something.
You can "mask" your mutable object under a read-only interface.
public class ClassWithMutable
{
public IImumutable Mutable { get { return _mutable; } }
private Mutable _mutable;
public ClassWithMutable()
{
_mutable = new Mutable()
{
Value = 1
};
}
}
public interface IImumutable
{
int Value { get; }
}
public class Mutable : IImumutable
{
public int Value { get; set; }
}
As long as your ClassWithMutable instance exposes the Mutable instance as an Immutable then the consumer can't easily change it. (I emphasize easily, because there's pretty much always a way that you can change it. It just depends on how hard you want to work.)

Clearing data before reading a new line

I have a considerable number of strings in my application that need to be cleared each time I get new data from my source. I'd like to use something akin to string.Empty, but I am unsure of how to implement this. Ideally, I'd also like to do this only once, rather than for each separate string.
Pseudo-code:
foreach (string in application)
{
this.empty
}
Am I thinking on the right track?
Some of my code is as follows:
classtoinstantiate
public string Str1;
private string str1 {get {return Str1;}}
public void DoStuff()
{
doStuff();
}
private void doStuff()
{
//dostuff
}
And Form1.cs
classtoinstantiate class1 = new classtoinstantiate();
class.DoStuff();
//I would like to then clear the *public* iteration of string Str1 here,
//before I DoStuff() again.
String.Empty represents a not null empty string.
If you want to clear a large amount of data (string/non string) you can encapsulate all of the variables in one class and create a Clean() method that goes through all the variables and clears them or instantiate that class when you need a fresh copy when you set the default values in the constructor.
The use of class.Empty is from what I understand to have a well defined instance of what is an empty instance.
Given your comments I get the feeling that you only want to clear the strings, have a look at this C# like pseudo code:
public void ClearString(IEnumerable<object> stuffToClear)
{
// go through all the objects to clear
foreach (var item in stuffToClear)
{
// get the properties to clear
var props = from prop in item.GetType().GetProperties()
where prop.PropertyType == typeof(string) // or another type or filter
select prop;
for (var p in props)
{
// clear it
p.SetValue(item, string.Empty);
}
}
}
Not that I'm writing this in freehand, all calls will surely not be correct.
That's the basic OOP concept: construct object when you need it, destroy at the end. Constructing part always deals with default values, which is exactly what you need.
For managed objects (string) simply create a new instance of a class holding all data what has to be reset (cleared):
class SomeDataStorage
{
// default is null
public string Data1 {get; set;}
private string _data2 = "default value";
public string Data2 { get {return _data2;} set {_data2 = value;}}
}
Then you construct this object when you need it
foreach (string in application)
{
var data = new SomeDataStorage(); // default values
...
}
It will be automagically destroyed when going out of scope (leaving { } or exiting function).
For unmanaged objects, implement IDisposable and consider to use using() { } often to auto-dispose.
You can have application-wide instance of SomeDataStorage. Simply assign a new object (construct new instance) to reset values to default.
To make it even more clear:
class App
{
public SomeDataStorage MyData;
public App()
{
Reset();
}
// call this when you need to init for the first time or simply reset to default
public void Reset()
{
MyData = new SomeDataStorage();
}
}
I suggest to put all your strings in to a class and dispose the object if you get new data
public class StringCollection
{
public string StringProp1 { get; set; }
public string StringProp2 { get; set; }
public string StringProp3 { get; set; }
public string StringProp4 { get; set; }
// .... more properties here
// this property won't be touched when clearing
public int SomeOtherProperty{ get; set; }
public void ClearStrings()
{
// returns all public properties
foreach (var prop in this.GetType().GetProperties())
{
// "clear" only properties of type String and those that have a public setter
if (prop.PropertyType == typeof(string) && prop.CanWrite)
prop.SetValue(this, string.Empty, null); // <- "clear" value of the property
}
}
or, in a more general manner - use extension methods:
public class StringCollection
{
public string StringProp1 { get; set; }
public string StringProp2 { get; set; }
public string StringProp3 { get; set; }
public string StringProp4 { get; set; }
public int SomeOtherProperty { get; set; }
}
public class SomeOtherClass
{
public string a1 { get; set; }
public string a2 { get; set; }
public string a3 { get; set; }
public DateTime d1 { get; set; }
public int SomeOtherProperty { get; set; }
}
public static class MyExtensions
{
public static void ClearStrings(this Object obj)
{
// returns all public properties
foreach (var prop in obj.GetType().GetProperties())
{
// "clear" only properties of type String and those that have a public setter
if (prop.PropertyType == typeof(string) && prop.CanWrite)
prop.SetValue(obj, string.Empty, null); // <- "clear" value of the property
}
}
}
use the code:
StringCollection scol2 = new StringCollection();
// ... do soemthing
scol2.ClearStrings();
SomeOtherClass obj = new SomeOtherClass();
// ... do something
obj.ClearStrings();

How can I invoke `EditorExtensions.EditorFor` in my HtmlHelper?

I use different Models in my CreateView, all inherit from BaseModel. To call the right EditorFor I have created a HtmlHelper that gets the Model and the actual property. But I donĀ“t know how to invoke it.
BaseModel:
public abstract class BaseModel
{
protected IEnumerable<PropertyInfo> PropertyInfoCache { get; set; }
protected IEnumerable<EnumeratedProperty> EnumeratedPropertyCache { get; set; }
protected BaseModel()
{
PropertyInfoCache = this.GetType().GetProperties();
EnumeratedPropertyCache = PropertyInfoCache.Select(p=> new EnumeratedProperty(p.Name,p.GetType()));
}
public IEnumerable<EnumeratedProperty> EnumerateProperties()
{
return EnumeratedPropertyCache;
}
public object GetPropertyValue(string PropertyName)
{
var property = PropertyInfoCache.SingleOrDefault(i=>i.Name==PropertyName);
if(property!=null)
return property.GetValue(this,null);
return null;
}
}
public class EnumeratedProperty
{
public string Name { get; private set; }
public Type Type { get; private set; }
public EnumeratedProperty(string PropertyName, Type PropertyType)
{
this.Name = PropertyName;
this.Type = PropertyType;
}
}
in my View:
#foreach (var property in Model.EnumerateProperties())
{
#Html.EditorForProperty(Model,property);
}
HtmlHelper:
public static class ExtensionMethods
{
public static MvcHtmlString EditorForProperty(this HtmlHelper html, BaseModel Model, EnumeratedProperty property)
{
// creates an error: The type arguments for method 'EditorFor' cannot be inferred from the usage. Try specifying the type arguments explicitly.
return System.Web.Mvc.Html.EditorExtensions.EditorFor(html, Model => Model.GetPropertyValue(property.Name) );
}
}
EditorFor recognizes the type of an object, so what you would want to do is extract the type from the value in the EnumeratedProperty class, instead of passing the class directly, and pass in the value from it too.

MEF Import Scenario

Hi
I have some problems in import scenarios example:
[Export(typeof(IICon))]
public class WriteInputData : IICon
{
[Import(typeof(IIOWriter))]
public IIOWriter IOWriter { get; set; }
public object Input { get; set; }
public void Process()
{
IOWriter.Write(Input);
}
}
Then i hawe two classes that implement interface IIOWriter like :
[Export(typeof(IIOWriter))]
public class FileWriter : IIOWriter
{
public string FilePath { get; set; }
public void Write(object data)
{
if (string.IsNullOrEmpty(FilePath))
FilePath = #"c:\test.txt";
var fl = new StreamWriter(FilePath, true);
fl.Write((string)data);
fl.Flush();
fl.Close();
}
public string Name
{
get { return "FileWriter"; }
}
}
[Export(typeof(IIOWriter))]
public class ConsoleWrite : IIOWriter
{
public void Write(object data)
{
Console.WriteLine((string)data);
}
public string Name
{
get { return "ConsoleWrite"; }
}
}
How can i let that to user so he can change that in runtime, so example whene he type select in ListBox FileWriter than the IIOWriter in WriteInputData will be injected FileWriter end so one..
Sorry for my bad english.
You probably need to supply some metadata to the export, such like:
[Export(typeof(IIOWriter)),
ExportMetadata("Name", "ConsoleWriter")]
public class ConsoleWriter : IIOWriter
{
}
The reason you need to do this, is that you need to know ahead of time what the user selection will match to. Because of this, you may want to refactor your design to remove the dependency on the IOWriter property:
[Export(typeof(IICon))]
public class WriteInputData : IICon
{
public object Input { get; set; }
public void Process(IIOWriter writer)
{
}
}
If you define your Process method to take in an instance, we can resolve it using the CompositionContainer. Firstly, define a metadata interface that matches your ExportMetadata value:
public interface INamedMetadata
{
string Name { get; }
}
And then, we can resolve the instance:
public IIOWriter GetWriter(string name)
{
return container
.GetExports<IIOWriter, INamedMetadata>()
.Where(e => e.Metadata.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
.Select(e => e.Value)
.FirstOrDefault();
}
Hope that points you in the right direction....

Categories