I have a class wherein I want to use Strings with a fixed size.
The reason for the fixed size is that the class "serializes" into a textfile
with values with a fixed length. I want to avoid to write foreach value a guard clause and instead have the class handle this.
So I have round about 30 properties which would look like this
public String CompanyNumber
{
get
{
return m_CompanyNumber.PadLeft(5, ' ');
}
set
{
if (value.Length > 5)
{
throw new StringToLongException("The CompanyNumber may only have 5 characters", "CompanyNumber");
}
m_CompanyNumber = value;
}
}
I would like to have a String that handles this by itself. Currently I have the following:
public class FixedString
{
String m_FixedString;
public FixedString(String value)
{
if (value.Length > 5)
{
throw new StringToLongException("The FixedString value may consist of 5 characters", "value");
}
m_FixedString= value;
}
public static implicit operator FixedString(String value)
{
FixedString fsv = new FixedString(value);
return fsv;
}
public override string ToString()
{
return m_FixedString.PadLeft(5,' ');
}
}
The problem I have with this solution is that I can't set the String length at "compile time".
It would be ideal if it would look something like this in the end
public FixedString<5> CompanyNumber { get; set; }
I would go further back and question the design. This solution mashes together two concerns--internal application state and storage format--that should remain separate.
You could decorate each string property with a MaxLengthAttribute and then validate to that, but your code for (de)serializing from your storage format should be completely separate. It could use the same attributes to glean the field lengths for storage (if that happy coincidence holds) but your internal representation shouldn't "know" about the storage details.
Make FixedString take the size as a constructor parameter, but not the value itself
public class FixedString
{
private string value;
private int length;
public FixedString(int length)
{
this.length = length;
}
public string Value
{
get{ return value; }
set
{
if (value.Length > length)
{
throw new StringToLongException("The field may only have " + length + " characters");
}
this.value = value;
}
}
}
Initilise it with your class, and just set the Value when it changes
public class MyClass
{
private FixedString companyNumber = new FixedString(5);
public string CompanyNumber
{
get{ return companyNumber.Value; }
set{ companyNumber.Value = value; }
}
}
You can define an Interface like this:
public interface ILength
{
int Value { get; }
}
Some struct that implements the interface:
public struct LengthOf5 : ILength
{
public int Value { get { return 5; } }
}
public struct LengthOf10 : ILength
{
public int Value { get { return 10; } }
}
And then:
public class FixedString<T> where T : struct, ILength
{
String m_FixedString;
public FixedString(String value)
{
if (value.Length > default(T).Value)
{
throw new ArgumentException("The FixedString value may consist of " + default(T).Value + " characters", "value");
}
m_FixedString = value;
}
public static implicit operator FixedString<T>(String value)
{
FixedString<T> fsv = new FixedString<T>(value);
return fsv;
}
public override string ToString()
{
return m_FixedString;
}
}
To be honest I don't know if i like this solution but is the best I can think to solve your problem.
You could put an attribute over your String property and then validate all of them at some time (maybe a button click or something like that).
using System.ComponentModel.DataAnnotations;
public class MyObject
{
[StringLength(5)]
public String CompanyName { get; set; }
}
public void Save(MyObject myObject)
{
List<ValidationResult> results = new List<ValidationResult>();
ValidationContext context = new ValidationContext(myObject, null, null);
bool isValid = Validator.TryValidateObject(myObject, context, results);
if (!isValid)
{
foreach (ValidationResult result in results)
{
// Do something
}
}
}
More about DataAnnotations here.
I think your original idea of creating a string of fixed length is a very valid one, strictly modelling the domain of your system and using the type system to verify it is an idea that I find very appealing. Questions like this seem to come up very often within the F# community.
Unfortunately something like the type definition you suggested (FixedString<5>) is not possible in the context of .NET.
Some of the answers so far have talked about workarounds, alternatives or other ideas, I'd like to instead answer why you can't do what you originally requested in C#.
First of all, lets look at how you could do this in an arbitrary language:
Templates: You could do something like this in C++ using the template system. As Eric Lippert puts it in his article on the differences between generics and templates, "You can think of templates as a fancy-pants search-and-replace mechanism" (https://blogs.msdn.microsoft.com/ericlippert/2009/07/30/whats-the-difference-part-one-generics-are-not-templates/).
.NET generics are, in many ways, far simpler by comparison. Generic types are allow you to parametrise over types but not over values and open types are resolved at runtime whereas templates are an entirely compile time construct.
Dependent Types: A few languages support a feature called dependent types (https://en.wikipedia.org/wiki/Dependent_type). This allows you to define types that depend upon values. Many languages that support this feature are geared toward theorem proving rather than general purpose development.
Idris is perhaps unusual in being a general purpose language under active development (albeit a little known one) which does support this feature (see http://www.idris-lang.org/).
C#
C# does not support either of these features so, unfortunately, you can't solve this problem in a way that can be rigorously verified by the compiler.
I think there are plenty of good suggestions covered here for how you might implement something like this in C# but they all boil down to run-time verification.
Related
I want to create a structure to store data consumed from a Web Service with the followind specs:
Response:
Field 1 - InstructionType: Can be 1 (PreferredDay), 2 (SVP), 3 (Neighbour)
Field 2: Some variable data. Its type depends on Field 1. So if:
Field 1 == 1 then Field 2 type will be of DateTime (dd.MM.yyyy)
Field 1 == 2 then Field 2 type will be of type string.
Field 1 == 3 then Field 2 type will be of type string
So, I started up with the following enum:
public enum InstructionType
{
None = 0,
PreferredDay = 1,
ServicePoint = 2,
Neighbour = 3
}
And the generic class:
public abstract class Instruction<T>
{
public InstructionType Type { get; private set; }
public T Data { get; private set; }
public Instruction(InstructionType type, T data)
{
this.Type = type;
this.Data = data;
}
}
and concrete classes:
public class PreferredDayInstruction : Instruction<DateTime>
{
public PreferredDayInstruction(DateTime data)
: base (InstructionType.PreferredDay, data) {}
}
public class ServicePointInstruction: Instruction<string>
{
public ServicePointInstruction(string data)
: base (InstructionType.ServicePoint, data) {}
}
public class NeughbourInstruction: Instruction<string>
{
public NeughbourInstruction(string data)
: base (InstructionType.Neighbour, data) {}
}
When parsing web service's response created a public function:
public Instruction DeliveryInstruction() <---Compiler error here "Instruction"
{
if (resultFromWebservice.Field1 == 1)
return new PreferredDayInstruction((DateTime)Field2);
if (resultFromWebservice.Field1 == 2)
return new ServicePointInstruction(Field2);
if (resultFromWebservice.Field1 == 3)
return new NeighbourInstruction(Field2);
}
and here is the problem. Can't return objects of generic type.
Tried with with Interface, factories, and other stuff, but allways with the same problem. So, is there any way to archieve this? maybe it's not possible or maybe is so easy I can't see now. Thanks in advance.
UPDATE:
Compiler error on BOLD Instruction
Error 1 Using the generic type 'NAMESPACE.Instruction' requires '1' type arguments
I forgot..I'm using .NET 3.5
It looks like you may be starting off with an intent to use generics rather than using them because you've identified a need. Often (not always) when that gets difficult it's because it didn't actually fit what you were trying to do.
What seems odd in this case is that you have both a generic type and an enum to indicate the type. This is likely to cause you a few problems.
First it looks like you're trying to create a one-size-fits all class to model different types of behaviors. That will start off confusing and get more confusing. Think of most classes that are part of the .NET framework, and imagine what would happen if they had properties like Field1 and Field2, and you couldn't tell from looking at them what they were for. And in one method they're used for one thing, but in a another case they mean something else.
Also, if you're trying to put different types of instructions in one class, that suggests that maybe you're going to try passing them all to one method, and that method figures out what to do, and maybe calls other methods. (I'm guessing that because of the enum. Perhaps you're going to handle the input differently depending on which value it contains.) That one method will get really hard to maintain.
I'd recommend waiting on generics until you're sure you need them. And if you have different types of instructions you're likely better off writing a different class for each one with the properties it needs and names that describe them, and writing methods for each of them to do what they need to do. If you need lots of classes, make lots of them.
It's very easy to fall into the trap of trying to solve problems that don't exist, like how do I write one class that covers a bunch of different needs. The answer usually that you don't need to. You'll get better results from writing more classes that each do fewer things.
Believe me that I tried to do my best to explain what was my problem and what I needed in order to solve it. In a nutshell, the question was quite simple. Is this possible or not? So, is there a way to return a common type for these 3 classes? Answer is no, as they don't share any root. They all derive from Instruction, but aren't compatible each other. That's what I learned from this experience.
As another example, lets take another .NET framework's generic type.
public class ListOfString : List<string> { }
public class ListOfInt : List<int> { }
public class ListOfDecimal : List<decimal> { }
And, in another place of the application, get a method who returns one of this List based on some logic:
public class Logic
{
public List<> GetList(Type t) <----This can't be done
{
if (t == typeof(string))
return new ListOfString();
if (t == typeof(int))
return new ListOfInt();
if (t == typeof(decimal))
return new ListOfDecimal();
else return null;
}
}
Please, keep in mind that this is just a stupid sample just to show what's the point of this post.
By the way, in the case of List the following can be done, because there is a non generic different version of IList:
public IList GetList(Type t)
{
....
}
But I can't think of a way to do this in my particular case.
Anyway, I finally followed another approach. I reallized that what I really wanted is to ensure Data property is valid. If it it's supposed to be a date there, ensure date is valid. Is it a string, ensure it has the right length or whatever rule it must follow.
So this is the final solution:
The enum:
public enum InstructionType
{
None = 0,
PreferredDay = 1,
ServicePoint = 2,
Neighbour = 3
}
The base class:
public abstract class Instruction
{
public InstructionType Type { get; private set; }
public string Data { get; private set; } <---Type String
public Instruction(InstructionType type, string data)
{
this.Type = type;
this.Data = IsValid(data) ? data : string.Empty;
}
public abstract bool IsValid(string data); <--the rule.
}
The concrete classes:
public class PreferredDayInstruction : Instruction
{
public PreferredDayInstruction(string date)
: base(InstructionType.PreferredDay, date) { }
public override bool IsValid(string data)
{
string[] formats = {"dd.MM.yyyy", "d.MM.yyyy",
"dd.MM.yy", "d.MM.yy"};
try
{
data = data.Replace('/', '.').Replace('-', '.');
var dateparts = data.Split('.');
DateTime date = new DateTime(Convert.ToInt32(dateparts[2]),
Convert.ToInt32(dateparts[1]),
Convert.ToInt32(dateparts[0]));
//DateTime.ParseExact(data, formats, null, System.Globalization.DateTimeStyles.AssumeLocal);
return true;
}
catch (Exception)
{
return false;
}
}
}
public class ServicePointInstruction : Instruction
{
public ServicePointInstruction(string data)
: base (InstructionType.ServicePoint, data) { }
public override bool IsValid(string data)
{
return ServicePointBarcodeValidator.Validate(data);
}
}
public class NeighbourInstruction : Instruction
{
public NeighbourInstruction(string data) :
base(InstructionType.Neighbour, data) { }
public override bool IsValid(string data)
{
return data.Length <= 70;
}
}
A factory class, who's responsability is to create and return the correct object based on the enum:
public static class DeliveryInstructionFactory
{
public static Instruction Create(int type, string data)
{
return Create((InstructionType)type, data);
}
public static Instruction Create(InstructionType type, string data)
{
switch (type)
{
case InstructionType.PreferredDay:
return new PreferredDayInstruction(data);
case InstructionType.ServicePoint:
return new ServicePointInstruction(data);
case InstructionType.Neighbour:
return new NeighbourInstruction(data);
default:
return null;
}
}
}
And finally, as now all of they share the same root, object can be created on webservice's response parser:
public Instruction DeliveryInstruction()
{
try
{
int instructionCode = int.Parse(observation.Substring(173,2));
string instructionData = observation.Substring(175, 10);
return DeliveryInstructionFactory.Create(instructionCode, instructionData); }
catch (Exception ex)
{
Log.Error("[ValidationBarcodeResponse] DeliveryInstructions aren't in the correct format", ex);
return null;
}
}
Hope this now fits on a Minimal, Complete, and Verifiable example
I'm learning C# and currently we're looking into OOP concepts. We've been given this question and I'm struggling to understand some parts of it.
The gist of the question is this.
Define a class named Operator.
That class should implement following methods.
IsPositive - Receives an integer type value and returns true if it
is positive, false otherwise.
IsDayOfWeek - Receives a date time value and a week day name (E.g.
Saturday) and returns true if the value represents the given week day
name, false otherwise.
GetWords - Receives a text containing words (say paragraphs) and
returns a single dimension string array with all words. An empty
string array if there is no word available in the text.
It should be able to derive from Operator class and then create objects from the derived class.
Developers are allowed to use these methods from derived class for a given type. In other words, 1st method could be used when type = ‘N’ (number), 2nd methods could be used when type is ‘D’ (date) and 3rd method could be used when type is ‘S’ (string) given. Hence, the type should be provided when instantiating the object and it should be available throughout the class operations.
I have sufficient knowledge to write the methods but what I don't understand is the part I have bold-ed. What does it mean by some method can be used when some type is given and the type should be provided when instantiating the object and it should be available throughout the class? Are they talking about Properties?
I have given it a go. Below is my code.
public class Operator
{
private int _n;
private DateTime _d;
private string _s;
public DataProcessor(int n, DateTime d, string s)
{
this.N = n;
this.D = d;
this.S = s;
}
public int N
{
set { _n = value; }
}
public DateTime D
{
set { _d = value; }
}
public string S
{
set { _s = value; }
}
public bool IsPositive()
{
//method code goes here
return false;
}
public bool IsDayOfWeek()
{
//method code goes here
return false;
}
}
I'm not sure if I'm going the right way. Can somebody please shed some light on this?
This is how I read it:
public class Operator
{
public char TypeChar { get; set; }
public Operator(char operatorType) { this.TypeChar = operatorType; }
public bool IsPositive(int N)
{
if (TypeChar != 'N')
throw new Exception("Cannot call this method for this type of Operator");
// method implementation code
}
// same for the other methods
}
public NumericOperator : Operator
{
public NumericOperator() : base('N') {}
}
I have a struct on the server-side with a layout like this:
struct SomeStruct
{
public string SomeString { get; set; };
public string SomeString1;
public string SomeString2;
public string SomeString3;
}
I am using a client/server model, and an instance of this struct gets referenced a lot of times as it has really important information (over 200 times).
The thing is that when some function gets called, the values inside this struct become null. I don't know why and it is been bugging me for a really long time.
I call a lot of methods before realizing that this values are null, so I don't know which section of my code nullify my strings.
I am using VS2012, but I have 2010 and 2008 ultimate as well. I was wondering if there is a way to perform a trigger when some section of code nullifies my strings.
I tried to add some properties like this, bot the exception was never thrown:
struct SomeStruct {
string somestr;
public string SomeString
{
get { return somestr; }
set
{
if (value == null)
{
throw new Exception("stirng is null");
}
somestr = value;
}
}
public string SomeString1;
public string SomeString2;
public string SomeString3;
}
Might not be important, but this is one of the structs I am using (the Name variable becomes null in some part of my code, and the rest turns into default()):
[ProtoContract]
public struct CharacterInformation
{
[ProtoMember(2)]
public string Name;
[ProtoMember(3)]
public IntegerVector2 Position;
[ProtoMember(5)]
public CharacterDirection Direction;
[ProtoMember(6)]
public CharacterStatus Status;
[ProtoMember(7)]
public CharacterClass Class;
[ProtoMember(8)]
public CharacterRace Race;
[ProtoMember(9)]
public CharacterType Type;
[ProtoMember(10)]
public CharacterFaction Faction;
[ProtoMember(11)]
public float MovementModifier;
[ProtoMember(12)]
public CharacterEquipment Equipment;
}
Edit: The only instance of this struct is created on a Sql-related function:
public CharacterServerInformation GetInformation(int charID)
{
CharacterServerInformation information = new CharacterServerInformation();
if (!authInstance.CharacterExists(charID))
{
// char doesn't exists
throw new Exception("Character doesn't exists");
}
information.ID = charID;
information.Experience = GetExperience(charID);
information.Info.Direction = CharacterDirection.Bottom;
information.Info.Name = authInstance.CharacterGetName(charID);
information.Info.Class = GetClass(charID);
information.Info.Faction = GetFaction(charID);
information.Info.Position = GetPosition(charID);
information.Info.Race = GetRace(charID);
information.Info.Status = GetStatus(charID);
information.Info.Type = GetType(charID);
information.Info.MovementModifier = 1f; // should store old movement modifier, but well, whatever
information.HealthLeft = GetHealthLastLogout(charID);
return information;
}
I suspect the problem is purely because you're using struct and not making a class. Since struct members are copied by value into methods and when returned from methods, including property getters, it's likely that you're "losing" the information by accidentally writing a new struct somewhere.
In this case, class seems is far more appropriate. If you read Choosing Between Classes and Structures, you'll see that struct should only be used when:
It logically represents a single value, similar to primitive types (integer, double, and so on).
It has an instance size smaller than 16 bytes.
It is immutable.
It will not have to be boxed frequently.
In your case, all of these criteria ( except maybe the last) are being violated, so class would be more appropriate.
I am trying to change the serializer in an existing WCF net.tcp project that uses shared entities on client & server. I am having a hard time figuring out protobuf-net(V2480)
The chart here says I can serialize private members but cannot find documentation to do that, is it possible without attributes? How do I enable graph mode(As Reference) as explained here
Will that solve the issue of protobuf triggering my changed items flag? For example I have a class
public enum FirstEnum
{
First = 0,
Second,
Third
}
public enum AnotherEnum
{
AE1 = 0,
AE2,
AE3
}
[Serializable()]
public class SomeClass
{
public int SomeClassId { get; set; }
public FirstEnum FEnum { get; set; }
public AnotherEnum AEnum { get; set; }
string thing;
public string Thing
{
get{return thing;}
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentNullException("Thing");
thing = value;
}
}
private decimal firstAmount;
public decimal FirstAmount
{
get{return firstAmount;}
set
{
if (value != firstAmount)
{
firstAmount = value;
changedItems.Add("FirstAmount changed");
}
}
}
private decimal secondAmount;
public decimal SecondAmount
{
get { return secondAmount; }
set
{
if (value != secondAmount)
{
secondAmount = value;
changedItems.Add("SecondAmount changed");
}
}
}
public decimal ThirdAmount { get { return SecondAmount - FirstAmount; } }
public DateTime? SomeDate { get; set; }
private List<string> changedItems = new List<string>();
public List<string> ChangedItems
{
get { return changedItems; }
}
public int PrivateSet { get; private set; }
public SomeClass() { }
public SomeClass(decimal first, decimal second)
{
FirstAmount = first;
SecondAmount = second;
}
public void ClearChangedItems()
{
changedItems.Clear();
}
When I deserialize it with (1000 items)
var model = CreateModel();
items = (List<SomeClass>)model.Deserialize(returnStream, null, typeof(List<SomeClass>));
2012-04-06 09:14:28.1222|DEBUG|ProtobufTEsts.Form1|ProtoBuf Number of changed items : 1000
With BinaryForrmatter
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
items = (List<SomeClass>)binaryFormatter.Deserialize(returnStream);
2012-04-06 09:14:28.1662|DEBUG|ProtobufTEsts.Form1|BinaryFormatter Number of changed items : 0
Is there a way to get protobuf to behave like the binaryFormatter but preserve the performance of protobuf?
How to allow for private serialization, this fails
public static TypeModel CreateModel()
{
RuntimeTypeModel model = TypeModel.Create();
///var metaType = RuntimeTypeModel.Default.Add(typeof(SomeClass), false);
model.Add(typeof(SomeClass), false)
.Add(1, "SomeClassId")
.Add(2, "FEnum")
.Add(3, "AEnum")
.Add(4, "Thing")
.Add(5, "FirstAmount")
.Add(6, "SecondAmount")
.Add(7, "SomeDate")
.Add(8, "PrivateSet");
TypeModel compiled = model.Compile();
return compiled;
}
Ah, I understand the issue now; this line is problematic:
TypeModel compiled = model.Compile();
return compiled;
If you use Compile(), it creates a formal assembly (in memory) that has to obey the usual rules of assemblies, and in particular: member accessibility. This means it can't access your private sertter.
Instead, use:
model.CompileInPlace();
return model;
This performs a partial compilation, but continues using DynamicMethod. This cheeky little critter has options to spoof its way past accessibility rules (much like reflection can), so it can continue to use the private setter.
Note that the model is also compiled-in-place (at a more granular level) on as as-needed basis, so this call to CompileInPlace is not strictly necessary, but helps do everything up-front an in advance.
For completeness, there is an additional Compile(string,string) overload that can be used to produce a separate serialization dll on disk, that can be referenced and used without any meta-programming at runtime.
Yes protobuf-net can serialize private fields, and do so without attributes. I'm not at a PC, so this may need tweaking:
var metaType = RuntimeTypeModel.Default.Add(typeof(SomeClass), false);
// for each field in a known order
metaType.Add(fieldName, someUniqueTag);
In attribute-driven usage, there is also ImplicitFields.AllFields which would automatically configure it for the usage you intend, but I haven't yet added an ImplicitFields helper method to MetaType. I will add that to my list!
Note: tag (=field) numbers are important to protobuf and it must be possible to reproduce the same number mappings when you deserialize.
Another option you might want to consider is (de)serialization callbacks, which allow you to know that it is currently serializing/deserializing (via before/after method invokes). This can be another way of disabling side-effects for an interval such as deserialization.
This is related to a prior question of mine C# Generic List conversion to Class implementing List<T>
I have the following code:
public abstract class DataField
{
public string Name { get; set; }
}
public class DataField<T> : DataField
{
public T Value { get; set; }
}
public static List<DataField> ConvertXML(XMLDocument data) {
result = (from d in XDocument.Parse(data.OuterXML).Root.Decendendants()
select new DataField<string>
{
Name = d.Name.ToString(),
Value = d.Value
}).Cast<DataField>().ToList();
return result;
}
This works however I would like to be able to modify the select portion of the LINQ query to be something like this:
select new DataField<[type defined in attribute of XML Element]>
{
Name = d.Name.ToString(),
Value = d.Value
}
Is this just a poor approach? is it possible? Any suggestions?
Here is a working solution: (You must specify fully qualified type names for your Type attribute otherwise you have to configure a mapping somehow...)
I used the dynamic keyword, you can use reflection to set the value instead if you do not have C# 4...
public static void Test()
{
string xmlData = "<root><Name1 Type=\"System.String\">Value1</Name1><Name2 Type=\"System.Int32\">324</Name2></root>";
List<DataField> dataFieldList = DataField.ConvertXML(xmlData);
Debug.Assert(dataFieldList.Count == 2);
Debug.Assert(dataFieldList[0].GetType() == typeof(DataField<string>));
Debug.Assert(dataFieldList[1].GetType() == typeof(DataField<int>));
}
public abstract class DataField
{
public string Name { get; set; }
/// <summary>
/// Instanciate a generic DataField<T> given an XElement
/// </summary>
public static DataField CreateDataField(XElement element)
{
//Determine the type of element we deal with
string elementTypeName = element.Attribute("Type").Value;
Type elementType = Type.GetType(elementTypeName);
//Instanciate a new Generic element of type: DataField<T>
dynamic dataField = Activator.CreateInstance(typeof(DataField<>).MakeGenericType(elementType));
dataField.Name = element.Name.ToString();
//Convert the inner value to the target element type
dynamic value = Convert.ChangeType(element.Value, elementType);
//Set the value into DataField
dataField.Value = value;
return dataField;
}
/// <summary>
/// Take all the descendant of the root node and creates a DataField for each
/// </summary>
public static List<DataField> ConvertXML(string xmlData)
{
var result = (from d in XDocument.Parse(xmlData).Root.DescendantNodes().OfType<XElement>()
select CreateDataField(d)).ToList();
return result;
}
}
public class DataField<T> : DataField
{
public T Value { get; set; }
}
You cannot do this easily in C#. The generic type argument has to specified at compile time. You can use reflection to do otherwise
int X = 1;
Type listype = typeof(List<>);
Type constructed = listype.MakeGenericType( X.GetType() );
object runtimeList = Activator.CreateInstance(constructed);
Here we have just created a List<int>. You can do it with your type
Different instances of a generic class are actually different classes.
I.e. DataField<string> and DataField<int> are not the same class at all(!)
This means, that you can not define the generic parameter during run-time, as it has to be determined during compile-time.
I would say this is a poor approach. In reality, even after you parse your XML file, you're not going to know what types of "DataFields" you have. You might as well just parse them as objects.
However, if you know that you're only ever going to have x number of types, you can do like so:
var Dictionary<string, Func<string, string, DataField>> myFactoryMaps =
{
{"Type1", (name, value) => { return new DataField<Type1>(name, Type1.Parse(value); } },
{"Type2", (name, value) => { return new DataField<Type2>(name, Type2.Parse(value); } },
};
Termit's answer is certainly excellent. Here is a little variant.
public abstract class DataField
{
public string Name { get; set; }
}
public class DataField<T> : DataField
{
public T Value { get; set; }
public Type GenericType { get { return this.Value.GetType(); } }
}
static Func<XElement , DataField> dfSelector = new Func<XElement , DataField>( e =>
{
string strType = e.Attribute( "type" ).Value;
//if you dont have an attribute type, you could call an extension method to figure out the type (with regex patterns)
//that would only work for struct
Type type = Type.GetType( strType );
dynamic df = Activator.CreateInstance( typeof( DataField<>).MakeGenericType( type ) );
df.Name = e.Attribute( "name" ).Value;
dynamic value = Convert.ChangeType( e.Value , type );
df.Value = value;
return df;
} );
public static List<DataField> ConvertXML( string xmlstring )
{
var result = XDocument.Parse( xmlstring )
.Root.Descendants("object")
.Select( dfSelector )
.ToList();
return result;
}
static void Main( string[] args )
{
string xml = "<root><object name=\"im1\" type=\"System.String\">HelloWorld!</object><object name=\"im2\" type=\"System.Int32\">324</object></root>";
List<DataField> dfs = ConvertXML( xml );
}
you can create generic type by reflection
var instance = Activator.CreateInstance( typeof(DataField)
.MakeGenericType(Type.GetType(typeNameFromAttribute) );
// and here set properties also by reflection
#Termit and #Burnzy put forward good solutions involving factory methods.
The problem with that is that you're loading up your parsing routine with a bunch of extra logic (more testing, more errors) for dubious returns.
Another way to do it would be to use a simplified string-based DataField with typed read methods - the top answer for this question.
An implementation of a typed-value method that would be nice but only works for value types (which does not include strings but does include DateTimes):
public T? TypedValue<T>()
where T : struct
{
try { return (T?) Convert.ChangeType(this.Value, typeof(T)); }
catch { return null; }
}
I'm assuming that you're wanting to use the type information to do things like dynamically assigning user-controls to the field, validation rules, correct SQL types for persistence etc.
I've done a lot of this sort of thing with approaches that seem a bit like yours.
At the end of the day you should seperate your metadata from your code - #Burnzy's answer chooses the code based on the metadata (a "type" attribute of the DataField element) and is a very simple example of this.
If you're dealing with XML, XSDs are a very useful and extensible form of metadata.
As far as what you store each field's data in - use strings because:
they are nullable
they can store partial values
they can store invalid values (makes telling the user to sort their act out more transparent)
they can store lists
special cases won't invade unrelated code because there aren't any
learn regular expressions, validate, be happy
you can convert them to stronger types really easily
I found it very rewarding to develop little frameworks like this - it is a learning experience and you'll come out understanding a lot more about UX and the reality of modelling from it.
There are four groups of test cases that I would advise you to tackle first:
Dates, Times, Timestamps (what I call DateTime), Periods (Timespan)
in particular, make sure you test having a different server locality from the client's.
lists - multi-select foreign keys etc
null values
invalid input - this generally involves retaining the original value
Using strings simplifies all this greatly because it allows you to clearly demarcate responsibilities within your framework. Think about doing fields containing lists in your generic model - it gets hairy rather quickly and it is easy to end up with a special case for lists in pretty much every method. With strings, the buck stops there.
Finally, if you want a solid implementation of this sort of stuff without having to do anything much, consider DataSets - old school I know - they do all sorts of wonderful things you wouldn't expect but you do have to RTFM.
The main downfall of that idea would be that it isn't compatible with WPF data binding - though my experience has been that reality isn't compatible with WPF data binding.
I hope I interpreted your intentions correctly - good luck either way :)
Unfortunately, there no inheritance relation between C<T> and C<string> for instance.
However, you can inherit from a common non-generic class and in addition to this implement a generic interface.
Here I use explicit interface implementation in order to be able to declare a Value property typed as object, as well as a more specifically typed Value property.
The Values are read-only and can only be assigned through a typed constructor parameter. My construction is not perfect, but type safe and doesn't use reflection.
public interface IValue<T>
{
T Value { get; }
}
public abstract class DataField
{
public DataField(string name, object value)
{
Name = name;
Value = value;
}
public string Name { get; private set; }
public object Value { get; private set; }
}
public class StringDataField : DataField, IValue<string>
{
public StringDataField(string name, string value)
: base(name, value)
{
}
string IValue<string>.Value
{
get { return (string)Value; }
}
}
public class IntDataField : DataField, IValue<int>
{
public IntDataField(string name, int value)
: base(name, value)
{
}
int IValue<int>.Value
{
get { return (int)Value; }
}
}
The list can then be declared with the abstract base class DataField as generic parameter:
var list = new List<DataField>();
switch (fieldType) {
case "string":
list.Add(new StringDataField("Item", "Apple"));
break;
case "int":
list.Add(new IntDataField("Count", 12));
break;
}
Access the strongly typed field through the interface:
public void ProcessDataField(DataField field)
{
var stringField = field as IValue<string>;
if (stringField != null) {
string s = stringField.Value;
}
}
While the other questions mostly proposed an elegant solution to convert your XML elements to a generic class instance, I'm going to deal with the consequences of taking the approach to model the DataField class as a generic like DataField<[type defined in attribute of XML Element]>.
After selecting your DataField instance into the list you want to use these fields. Her polymorphism comes into play! You want to iterate your DataFields an treat them in a uniform way. Solutions that use generics often end up in a weird switch/if orgy since there is no easy way to associate behavior based on the generic type in c#.
You might have seen code like this (I'm trying to calculate the sum of all numeric DataField instances)
var list = new List<DataField>()
{
new DataField<int>() {Name = "int", Value = 2},
new DataField<string>() {Name = "string", Value = "stringValue"},
new DataField<float>() {Name = "string", Value = 2f},
};
var sum = 0.0;
foreach (var dataField in list)
{
if (dataField.GetType().IsGenericType)
{
if (dataField.GetType().GetGenericArguments()[0] == typeof(int))
{
sum += ((DataField<int>) dataField).Value;
}
else if (dataField.GetType().GetGenericArguments()[0] == typeof(float))
{
sum += ((DataField<float>)dataField).Value;
}
// ..
}
}
This code is a complete mess!
Let's go try the polymorphic implementation with your generic type DataField and add some method Sum to it that accepts the old some and returns the (possibly modified) new sum:
public class DataField<T> : DataField
{
public T Value { get; set; }
public override double Sum(double sum)
{
if (typeof(T) == typeof(int))
{
return sum + (int)Value; // Cannot really cast here!
}
else if (typeof(T) == typeof(float))
{
return sum + (float)Value; // Cannot really cast here!
}
// ...
return sum;
}
}
You can imagine that your iteration code gets a lot clearer now but you still have this weird switch/if statement in you code. And here comes the point: Generics do not help you here it's the wrong tool at the wrong place. Generics are designed in C# for giving you compile time type safety to avoid potential unsafe cast operations. They additionally add to code readability but that's not the case here :)
Let's take a look at the polymorphic solution:
public abstract class DataField
{
public string Name { get; set; }
public object Value { get; set; }
public abstract double Sum(double sum);
}
public class IntDataField : DataField
{
public override double Sum(double sum)
{
return (int)Value + sum;
}
}
public class FloatDataField : DataField
{
public override double Sum(double sum)
{
return (float)Value + sum;
}
}
I guess you will not need too much fantasy to imagine how much adds to your code's readability/quality.
The last point is how to create instances of these classes. Simply by using some convention TypeName + "DataField" and Activator:
Activator.CreateInstance("assemblyName", typeName);
Short Version:
Generics is not the appropriate approach for your problem because it does not add value to the handling of DataField instances. With the polymorphic approach you can work easily with the instances of DataField!
It's not impossible as you can do this with reflection. But this isn't what generics were designed for and isn't how it should be done. If you're going to use reflection to make the generic type, you may as well not use a generic type at all and just use the following class:
public class DataField
{
public string Name { get; set; }
public object Value { get; set; }
}
You'll need to insert the logic for determining the data type from your XML and add all the types you need to use but this should work:
result = (from d in XDocument.Parse(data.OuterXML).Root.Descendants()
let isString = true //Replace true with your logic to determine if it is a string.
let isInt = false //Replace false with your logic to determine if it is an integer.
let stringValue = isString ? (DataField)new DataField<string>
{
Name = d.Name.ToString(),
Value = d.Value
} : null
let intValue = isInt ? (DataField)new DataField<int>
{
Name = d.Name.ToString(),
Value = Int32.Parse(d.Value)
} : null
select stringValue ?? intValue).ToList();