Formatted string as datatype - c#

I have a format for certain Ids and I'd rather have a custom datatype for them rather than store them as a string.
How is this done in C#?
Is this a good idea in the first place?
An example below should explain what I mean:
Id format (D for digit, C for alphabet char): CCDDDD
public ItemId id { get; set; }
...
public class ItemId {
// somehow declare the format here
}

You could wrap a class around your string ID which takes a input string as constructor parameter. This way you can also put methods in your class to provide extra functionality etc, and always have the formatting in one place. Simple example:
public class ItemId
{
private string _id;
public string ID
{
get { return _value; }
set
{
//do some formatting here
_id= value;
}
}
public ItemId(string id)
{
ID = id
}
public override string ToString()
{
//do some extra formatting here if needed
return Value;
}
}
Because you can only manipulate the real ID through the public setter, you can have your formatting and validation logic in one single place. Hope this helps you a bit. I think it's a good idea because a class ItemId is more meaningful then just a string, and also a lot easier to extend or change functionality in the future.
For example you can check the input with regex, and throw your own exception if input does not match your format. That gives you meaningful information at runtime. Also you can add xml comment to the public setter, so if you or anyone uses it, it's clear what the ID should look like.

You can also do it by implementing your extensions, which I would prefer than overriding ToString method.
For Example
public static class Extension
{
public static string MyFormat(this string str)
{
//do sth with your string
return str;
}
}
And you can use this extension like
string abc = "";
abc.MyFormat();

Related

Object Oriented Programming - how to avoid duplication in processes that differ slightly depending on a variable

Something that comes up quite a lot in my current work is that there is a generalised process that needs to happen, but then the odd part of that process needs to happen slightly differently depending on the value of a certain variable, and I'm not quite sure what's the most elegant way to handle this.
I'll use the example that we usually have, which is doing things slightly differently depending on the country we're dealing with.
So I have a class, let's call it Processor:
public class Processor
{
public string Process(string country, string text)
{
text.Capitalise();
text.RemovePunctuation();
text.Replace("é", "e");
var split = text.Split(",");
string.Join("|", split);
}
}
Except that only some of those actions need to happen for certain countries. For example, only 6 countries require the capitalisation step. The character to split on might change depending on the country. Replacing the accented 'e' might only be required depending on the country.
Obviously you could solve it by doing something like this:
public string Process(string country, string text)
{
if (country == "USA" || country == "GBR")
{
text.Capitalise();
}
if (country == "DEU")
{
text.RemovePunctuation();
}
if (country != "FRA")
{
text.Replace("é", "e");
}
var separator = DetermineSeparator(country);
var split = text.Split(separator);
string.Join("|", split);
}
But when you're dealing with all the possible countries in the world, that becomes very cumbersome. And regardless, the if statements make the logic harder to read (at least, if you imagine a more complex method than the example), and the cyclomatic complexity starts to creep up pretty fast.
So at the moment I'm sort of doing something like this:
public class Processor
{
CountrySpecificHandlerFactory handlerFactory;
public Processor(CountrySpecificHandlerFactory handlerFactory)
{
this.handlerFactory = handlerFactory;
}
public string Process(string country, string text)
{
var handlers = this.handlerFactory.CreateHandlers(country);
handlers.Capitalier.Capitalise(text);
handlers.PunctuationHandler.RemovePunctuation(text);
handlers.SpecialCharacterHandler.ReplaceSpecialCharacters(text);
var separator = handlers.SeparatorHandler.DetermineSeparator();
var split = text.Split(separator);
string.Join("|", split);
}
}
Handlers:
public class CountrySpecificHandlerFactory
{
private static IDictionary<string, ICapitaliser> capitaliserDictionary
= new Dictionary<string, ICapitaliser>
{
{ "USA", new Capitaliser() },
{ "GBR", new Capitaliser() },
{ "FRA", new ThingThatDoesNotCapitaliseButImplementsICapitaliser() },
{ "DEU", new ThingThatDoesNotCapitaliseButImplementsICapitaliser() },
};
// Imagine the other dictionaries like this...
public CreateHandlers(string country)
{
return new CountrySpecificHandlers
{
Capitaliser = capitaliserDictionary[country],
PunctuationHanlder = punctuationDictionary[country],
// etc...
};
}
}
public class CountrySpecificHandlers
{
public ICapitaliser Capitaliser { get; private set; }
public IPunctuationHanlder PunctuationHanlder { get; private set; }
public ISpecialCharacterHandler SpecialCharacterHandler { get; private set; }
public ISeparatorHandler SeparatorHandler { get; private set; }
}
Which equally I'm not really sure I like. The logic is still somewhat obscured by all of the factory creation and you can't simply look at the original method and see what happens when a "GBR" process is executed, for example. You also end up creating a lot of classes (in more complex examples than this) in the style GbrPunctuationHandler, UsaPunctuationHandler, etc... which means that you have to look at several different classes to figure out all of the possible actions that could happen during punctuation handling. Obviously I don't want one giant class with a billion if statements, but equally 20 classes with slightly differing logic also feels clunky.
Basically I think I've got myself into some sort of OOP knot and don't quite know a good way of untangling it. I was wondering if there was a pattern out there that would help with this type of process?
I would suggest encapsulating all options in one class:
public class ProcessOptions
{
public bool Capitalise { get; set; }
public bool RemovePunctuation { get; set; }
public bool Replace { get; set; }
public char ReplaceChar { get; set; }
public char ReplacementChar { get; set; }
public bool SplitAndJoin { get; set; }
public char JoinChar { get; set; }
public char SplitChar { get; set; }
}
and pass it into the Process method:
public string Process(ProcessOptions options, string text)
{
if(options.Capitalise)
text.Capitalise();
if(options.RemovePunctuation)
text.RemovePunctuation();
if(options.Replace)
text.Replace(options.ReplaceChar, options.ReplacementChar);
if(options.SplitAndJoin)
{
var split = text.Split(options.SplitChar);
return string.Join(options.JoinChar, split);
}
return text;
}
When the .NET framework set out to handle these sorts of problems, it didn't model everything as string. So you have, for instance, the CultureInfo class:
Provides information about a specific culture (called a locale for unmanaged code development). The information includes the names for the culture, the writing system, the calendar used, the sort order of strings, and formatting for dates and numbers.
Now, this class may not contain the specific features that you need, but you can obviously create something analogous. And then you change your Process method:
public string Process(CountryInfo country, string text)
Your CountryInfo class can then have a bool RequiresCapitalization property, etc, that helps your Process method direct its processing appropriately.
Maybe you could have one Processor per country?
public class FrProcessor : Processor {
protected override string Separator => ".";
protected override string ProcessSpecific(string text) {
return text.Replace("é", "e");
}
}
public class UsaProcessor : Processor {
protected override string Separator => ",";
protected override string ProcessSpecific(string text) {
return text.Capitalise().RemovePunctuation();
}
}
And one base class to handle common parts of the processing:
public abstract class Processor {
protected abstract string Separator { get; }
protected virtual string ProcessSpecific(string text) { }
private string ProcessCommon(string text) {
var split = text.Split(Separator);
return string.Join("|", split);
}
public string Process(string text) {
var s = ProcessSpecific(text);
return ProcessCommon(s);
}
}
Also, you should rework your return types because it won't compile as you wrote them - sometimes a string method doesn't return anything.
You can create a common interface with a Process method...
public interface IProcessor
{
string Process(string text);
}
Then you implement it for each country...
public class Processors
{
public class GBR : IProcessor
{
public string Process(string text)
{
return $"{text} (processed with GBR rules)";
}
}
public class FRA : IProcessor
{
public string Process(string text)
{
return $"{text} (processed with FRA rules)";
}
}
}
You can then create a common method for instantiating and executing each country related class...
// also place these in the Processors class above
public static IProcessor CreateProcessor(string country)
{
var typeName = $"{typeof(Processors).FullName}+{country}";
var processor = (IProcessor)Assembly.GetAssembly(typeof(Processors)).CreateInstance(typeName);
return processor;
}
public static string Process(string country, string text)
{
var processor = CreateProcessor(country);
return processor?.Process(text);
}
Then you just need to create and use the processors like so...
// create a processor object for multiple use, if needed...
var processorGbr = Processors.CreateProcessor("GBR");
Console.WriteLine(processorGbr.Process("This is some text."));
// create and use a processor for one-time use
Console.WriteLine(Processors.Process("FRA", "This is some more text."));
Here's a working dotnet fiddle example...
You place all the country-specific processing in each country class. Create a common class (in the Processing class) for all the actual individual methods, so each country processor becomes a list of other common calls, rather than copy the code in each country class.
Note: You'll need to add...
using System.Assembly;
in order for the static method to create an instance of the country class.
A few versions ago, the C# swtich was given full support for pattern matching. So that "multiple countries match" case is easily done. While it still has no fall through ability, one input can match multiple cases with pattern matching. It could maybe make that if-spam a bit clearer.
Npw a switch can usually be replaced with a Collection. You need to be using Delegates and a Dictionary. Process can be replaced with.
public delegate string ProcessDelegate(string text);
Then you could make a Dictionary:
var Processors = new Dictionary<string, ProcessDelegate>(){
{ "USA", EnglishProcessor },
{ "GBR", EnglishProcessor },
{ "DEU", GermanProcessor }
}
I used functionNames to hand in the Delegate. But you could use the Lambda syntax to provide the entire code there. That way you could just hide that whole Collection like you would any other large collection. And the code becomes a simple lookup:
ProcessDelegate currentProcessor = Processors[country];
string processedString = currentProcessor(country);
Those are pretty much the two options. You may want to consider using Enumerations instead of strings for the matching, but that is a minor detail.
I would perhaps (depending on the details of your use-case) go with the Country being a "real" object instead of a string. The keyword is "polymorphism".
So basically it would look like this:
public interface Country {
string Process(string text);
}
Then you can create specialized countries for those that you need. Note: you don't have to create Country object for all countries, you can have LatinlikeCountry, or even GenericCountry. There you can collect what should be done, even re-using others, like:
public class France {
public string Process(string text) {
return new GenericCountry().process(text)
.replace('a', 'b');
}
}
Or similar. Country may be actually Language, I'm not sure about the use-case, but I you get the point.
Also, the method of course should not be Process() it should be the thing that you actually need done. Like Words() or whatever.
You want to delegate to (nod to chain of responsibility) something that knows about its own culture. So use or make a Country or CultureInfo type construct, as mentioned above in other answers.
But in general and fundamentally your problem is you are taking procedural constructs like 'processor' and applying them to OO. OO is about representing real world concepts from a business or problem domain in software. Processor does not translate to anything in the real world apart from software itself. Whenever you have classes like Processor or Manager or Governor, alarm bells should ring.
I was wondering if there was a pattern out there that would help with
this type of process
Chain of reponsibility is the kind of thing you may be looking for but in OOP is somewhat cumbersome...
What about a more functional approach with C#?
using System;
namespace Kata {
class Kata {
static void Main() {
var text = " testing this thing for DEU ";
Console.WriteLine(Process.For("DEU")(text));
text = " testing this thing for USA ";
Console.WriteLine(Process.For("USA")(text));
Console.ReadKey();
}
public static class Process {
public static Func<string, string> For(string country) {
Func<string, string> baseFnc = (string text) => text;
var aggregatedFnc = ApplyToUpper(baseFnc, country);
aggregatedFnc = ApplyTrim(aggregatedFnc, country);
return aggregatedFnc;
}
private static Func<string, string> ApplyToUpper(Func<string, string> currentFnc, string country) {
string toUpper(string text) => currentFnc(text).ToUpper();
Func<string, string> fnc = null;
switch (country) {
case "USA":
case "GBR":
case "DEU":
fnc = toUpper;
break;
default:
fnc = currentFnc;
break;
}
return fnc;
}
private static Func<string, string> ApplyTrim(Func<string, string> currentFnc, string country) {
string trim(string text) => currentFnc(text).Trim();
Func<string, string> fnc = null;
switch (country) {
case "DEU":
fnc = trim;
break;
default:
fnc = currentFnc;
break;
}
return fnc;
}
}
}
}
NOTE:
It does not have to be all static of course. If Process class need state you can use a instanced class or partially applied function ;) .
You can build the Process for each country on startup, store each one in a indexed collection and retrieve them when needed with O(1) cost.
I’m sorry that I long ago coined the term “objects” for this topic because
it gets many people to focus on the lesser idea. The big idea is
messaging.
~ Alan Kay, On Messaging
I would simply implement routines Capitalise, RemovePunctuation etc. as subprocesses that can be messaged with a text and country parameters, and would return a processed text.
Use dictionaries to group countries that fit a specific attribute (if you prefer lists, that would work as well with only a slight performance cost). For example: CapitalisationApplicableCountries and PunctuationRemovalApplicableCountries.
/// Runs like a pipe: passing the text through several stages of subprocesses
public string Process(string country, string text)
{
text = Capitalise(country, text);
text = RemovePunctuation(country, text);
// And so on and so forth...
return text;
}
private string Capitalise(string country, string text)
{
if ( ! CapitalisationApplicableCountries.ContainsKey(country) )
{
/* skip */
return text;
}
/* do the capitalisation */
return capitalisedText;
}
private string RemovePunctuation(string country, string text)
{
if ( ! PunctuationRemovalApplicableCountries.ContainsKey(country) )
{
/* skip */
return text;
}
/* do the punctuation removal */
return punctuationFreeText;
}
private string Replace(string country, string text)
{
// Implement it following the pattern demonstrated earlier.
}
I feel that the information about the countries should be kept in data, not in code. So instead of a CountryInfo class or CapitalisationApplicableCountries dictionary, you could have a database with a record for each country and a field for each processing step, and then the processing could go through the fields for a given country and process accordingly. The maintenance is then mainly in the database, with new code only needed when new steps are needed, and the data can be human readable in the database.
This assumes the steps are independent and don't interfere with each other; if that is not so things are complicated.

C# Get first non-empty property by priority

I've currently got a class that has multiple string properties. One or more of them may have a value. What I need to do is get the first non-empty property, based off some priority. Here is an example of what I envision the class would look like:
public class MyClass
{
[Priority(1)]
public string HighestPriority { get; set; }
[Priority(2)]
public string MiddlePriority { get; set; }
[Priority(3)]
public string LowestPriority { get; set; }
}
Or, maybe use another enum property that can be used to determine the highest one that is set?
public enum Priority
{
HighestPriority,
MiddlePriority,
LowestPriority
}
public class MyClass
{
private string highestPriority;
public string HighestPriority
{
get;
set
{
highestPriority = value;
HighestSetProperty = Priority.HighestPriority;
}
}
private string middlePriority;
public string MiddlePriority
{
get;
set
{
middlePriority = value;
if (HighestSetProperty != Priority.HighestPriority)
HighestSetProperty = Priority.MiddlePriority;
}
}
private string lowestPriority;
public string LowestPriority
{
get;
set
{
lowestPriority = value;
if (HighestSetProperty != Priority.HighestPriority || HighestSetProperty != Priority.MiddlePriority)
HighestSetProperty = Priority.LowestPriority;
}
}
public Priority HighestSetProperty { get; set; }
}
Then, use HighestSetProperty to set the string in a switch statement?
So far though, the only way I know of to find the first non-empty property, without using some form of priority attribute like above, is something like this:
string highestPriorityProp = String.IsNullOrWhitespace(HighestPriority) ? (String.IsNullOrWhitespace(MiddlePriority) ? LowestPriority : MiddlePriority) : HighestPriority;
Which is obviously messy and doesn't scale well if more properties are added.
So, what is the best way (in terms of readability, speed and scalability) of doing this? Thanks in advance.
EDIT: Let's go for cleanliness and scalability over speed.
EDIT: The duplicate question doesn't actually answer my question. As the properties may not be in the order of priority. Hence, the first one that is looped through may be a lower priority than the highest one set.
For instance:
public class MyClass
{
public string MiddlePriority { get; set; }
public string HighPriority { get; set; }
}
EDIT: Thanks for the help everyone. As mjwills and I have discussed in the comments, The accepted answer would suit my needs as I have only around 6 properties in my class. But, if there were more, the duplicate answer is probably the best way to go.
Normally you can do something like this with the coalesce operator:
var highest = high ?? medium ?? low;
But it seems you want to treat "null" and "empty string" the same. So write a function that converts empty strings to nulls:
Func<string,string> func = s => s == "" ? null : s;
Then coalesce over that:
var highest = func(HighestPriority) ?? func(MediumPriority) ?? func(LowPriority);
I recommend this over using attributes and reflection. Yes you'll have to modify the code when you add a new property, but you're already modifying the code to add a property itself-- there is little point is making only half the solution automatic. While using a "flexible" solution and Reflection is a common temptation, in my experience it is usually counterproductive in the long term.
If you are married to the idea of scaling and adsorbing more and more properties automatically, I suggest using a List or Dictionary and using something like First().
I would go with John Wus answer and use coalesce operator, but there are other possiblities to get yourself a null instead of an empty string:
Create a extensionmethods class and extend the 'string' class:
public static class ExtensionMethod
{
public static string ConvertEmptyToNull(this string str)
{
return string.IsNullOrEmpty(str) ? null : str;
}
}
Than use coalesce operator:
var highest = HighestPriority.ConvertEmptyToNull() ?? MediumPriority.ConvertEmptyToNull() ?? LowPriority.ConvertEmptyToNull();
But in your case i would rather implement the getter of your properties since you have private fields for your properties.
private string highestPriority;
public string HighestPriority
{
get
{
return string.IsNullOrEmpty(highestPriority) ? null : highestPriority;
}
set
{
highestPriority = value;
HighestSetProperty = Priority.HighestPriority;
}
}
Now your coalesce chain will look cleaner:
var highest = HighestPriority ?? MediumPriority ?? LowPriority;

Enum properties, Web API, JSON deserialization and typos

Let's say we got a class like this one:
public class Person
{
[JsonConstructor]
public Person(string name, DayOfWeek bornOnDay) => (Name, BornOnDay) = (name, bornOnDay);
public string Name { get; protected set; }
public DayOfWeek BornOnDay { get; protected set; }
}
And an endpoint like this one:
[HttpPost]
[Route("api/people")]
public IHttpActionResult PostPerson([FromBody]List<Person> people)
{
// whatever
}
I've noticed that if I make a typo in enum type when making a request - no error occurs, only the object that has the erroneous enum won't be deserialized.
For instance, the request body may look like this:
[{"name":"John", "bornOnDay":"Moonday",},
{"name":"Mark", "bornOnDay":"Friday",},]
Endpoint will receive the list containing one Person, Mark, born on Friday.
I would like to make the post operation either happen completely, or fail, not with only with the objects without enum typos.. Is there a way to do it, without receiving a string instead of DayOfWeek, and then using Enum.TryParse(...) to determine that the input was erroneous?
Edit:
I actually want the error to occur, and be detectable, so I can return 400 code to the client.
you can do like this , you can create enum value from string you have inputted
public class Person
{
private string day;
[JsonConstructor]
public Person(string name, string bornOnDay){
this.Name = name;
this.day = bornOnDay;
}
public string Name { get; protected set; }
public DayOfWeek BornOnDay {
get {
DayOfWeek weekday;
if(Enum.TryParse(day, true, out weekday))
return weekday;
else
return DayOfWeek.None;//add none if no able to parse
}
}
Note : None is added according to Null Object Pattern to avoid issues related to null value in system.
What about this:
public class Person
{
[JsonProperty]
public string Name { get; protected set; }
[JsonProperty]
private string _BornOnDay { set {
//try parse the string, if not successful, throw a nicely
//formatted error with the original string and what you expect,
//if parse is successful, set the value to BornOnDay;
}}
[JsonIgnore]
public DayOfWeek BornOnDay { get; protected set; }
}
Not sure about the JsonConstructor attribute, never needed to use it. If you do need it and if I understand it correctly you can have something similar with it. Deserializer should give a string into constructor, and then inside of it, you do parsing logic for the enum yourself.

String Object with fixed length C#

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.

get;set; with DateTime to validate with .TryParse?

I am attempting to move validation of input data into the get;set; of a class struct.
public void PlotFiles()
{
List<DTVitem.item> dataitems;
DTVitem.item i;
DateTime.TryParse("2012/01/01", out i.dt);
DateTime.TryParse("04:04:04", out i.t);
int.TryParse("455", out i.v);
dataitems.Add(i);
}
The struct is declared in a separate class (probably unnecessary):
public partial class DTVitem
{
public struct item
{
public DateTime dt;
public DateTime t;
public int v;
}
}
Every time I set DTVitem.item.dt, DTVitem.item.t, or DTVitem.item.v, I wish it to perform the relevant .TryParse() to validate the property contents.
However, when I attempt to use TryParse() as follows (attempting to wrap my head around this example from MSDN):
public partial class DTVitem
{
private DateTime _datevalue;
public string dt
{
get { return _datevalue; }
set { DateTime.TryParse(value, out _datevalue) ;}
}
}
I receive the error that _datevalue is a DateTime and cannot be converted to a string. The reason is obviously that the return path must return the type of dt in this instance (a string). However, I have attempted to massage this a few different ways, and am not able to hack it.
How do I achieve my goal of validating a string value as a DateTime when setting it as a property of an instance of the struct?
Is using set as I am attempting to the best way?
I can see that there is a lot of value in using get;set; for validation and would really like to understand it.
Thanks very much,
Matt
[edit]
Thanks to Jon Skeet below for pointing out the err of my ways.
Here's another thread on problems with mutable structs, and another speaking about instantiating a struct. Note structs are value types.
I believe the rest of what he was pointing out is sort of just agreeing that burying the struct way far away isn't necessary, and I should review why I'm doing it.
[solution]
I've taken into account some recommendations below and come up with the following:
public partial class DTVitem
{
private DateTime _dtvalue, _tvalue;
private int _vvalue;
public string dt
{
get { return _dtvalue.ToString(); }
set { DateTime.TryParse(value, out _dtvalue); }
}
public string t
{
get { return _tvalue.ToString(); }
set { DateTime.TryParse(value, out _tvalue); }
}
public string v
{
get { return _vvalue.ToString(); }
set { int.TryParse(value, out _vvalue); }
}
}
Inside my program class, I've instantiated and set with the following:
DTVitem item = new DTVitem();
item.dt = "2012/01/01";
item.t = "04:04:04";
item.v = "455";
So I opted not to use a struct, but a class; or really an instance of the class.
A property can only have one type. If you want the property to be of type string, then you can implement it this way:
public partial class DTVitem
{
private DateTime _datevalue;
public string dt
{
get { return _datevalue.ToString(); }
set { DateTime.TryParse(value, out _datevalue) ;}
}
}
However, using TryParse() will mean that the setter will not throw an exception if the DateTime is invalid. If you want it to do this, use DateTime.Parse() instead.
public partial class DTVitem
{
private DateTime _datevalue;
public string dt
{
get { return _datevalue.ToString(); }
set { DateTime.TryParse(value, out _datevalue) ;}
}
}
You're just missing a type convertion in the get. _datevalue is a DateTime but your property's a string.
get { return _datevalue.ToString(); } //or .toShortDateString() or ToShorttimeString()
The get;set; must have the same type. Your get returns a datetime when it expects a string, hence the error.
just use an explicit method bool setDate(String datestring) and put your code there. You can return a bool from the tryparse to let you know if it was successful.
Other (design mostly) issues aside, just getting to the problem of returning _datevalue as string, you can simply do something like:
public string dt
{
get { return _datevalue.ToString(); }
set { if(!DateTime.TryParse(value, out _datevalue)) /* Error recovery!! */ ;}
}
>>> You may also want to check the docs for DateTime.ToString() and see what format you want to get your string in when accessing the property.

Categories