95fd293467e8e89e44701d09eead51d4

Ok take it easy on me, I just started c# and I'd love everyone's feed back on the following extension I made. I'd love to hear about best practices, speed improvements, or just better ways to code what I did. I came from a coldfusion background so I was trying to code in some of the methods I missed in cf as well as a few nice shortcut methods.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
namespace Utilities
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Text.RegularExpressions;

    public static class StringExt
    {

        public static bool IsEmail(this string inputstr)
        {
            // exit if the string is null or empty
            if (inputstr.IsEmpty())
                return false;

            string strRegex = @"^([a-zA-Z0-9_\-\.\+]+)@((\[[0-9]{1,3}" +
                  @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
                  @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";

            return inputstr.MatchRegex(strRegex);
        }

        public static bool IsZip(this string inputstr)
        {
            // exit if the string is null or empty
            if (inputstr.IsEmpty())
                return false;

            string strRegex = @"^\d{5}$|^\d{5}-\d{4}$";

            return inputstr.MatchRegex(strRegex);
        }

        public static bool IsStateAbbrv(this string inputstr)
        {
            return inputstr.IsStateAbbrv(false);
        }


        public static bool IsStateAbbrv(this string inputstr, bool CaseSensitive)
        {
            // exit if the string is null or empty
            if (inputstr.IsEmpty())
                return false;

            string strRegex = @"^((AL)|(AK)|(AS)|(AZ)|(AR)|(CA)|(CO)|(CT)|(DE)|(DC)|(FM)|(FL)|" +
                @"(GA)|(GU)|(HI)|(ID)|(IL)|(IN)|(IA)|(KS)|(KY)|(LA)|(ME)|(MH)|(MD)|(MA)|(MI)|" +
                @"(MN)|(MS)|(MO)|(MT)|(NE)|(NV)|(NH)|(NJ)|(NM)|(NY)|(NC)|(ND)|(MP)|(OH)|(OK)|" +
                @"(OR)|(PW)|(PA)|(PR)|(RI)|(SC)|(SD)|(TN)|(TX)|(UT)|(VT)|(VI)|(VA)|(WA)|(WV)|" +
                @"(WI)|(WY))$";

            return inputstr.MatchRegex(strRegex, CaseSensitive);
        }

        public static bool IsStateName(this string inputstr)
        {
            return inputstr.IsStateName(false);
        }

        public static bool IsStateName(this string inputstr, bool CaseSensitive)
        {
            // exit if the string is null or empty
            if (inputstr.IsEmpty())
                return false;

            string strRegex = @"^((Alabama)|(Alaska)|(AmericanSamoa)|(Arizona)|(Arkansas)|(California)|" +
                @"(Colorado)|(Connecticut)|(Delaware)|(DistrictofColumbia)|(Florida)|(Georgia)|(Guam)|" +
                @"(Hawaii)|(Idaho)|(Illinois)|(Indiana)|(Iowa)|(Kansas)|(Kentucky)|(Louisiana)|(Maine)|" +
                @"(Maryland)|(Massachusetts)|(Michigan)|(Minnesota)|(Mississippi)|(Missouri)|(Montana)|" +
                @"(Nebraska)|(Nevada)|(NewHampshire)|(NewJersey)|(NewMexico)|(NewYork)|(NorthCarolina)|" +
                @"(NorthDakota)|(NorthernMarianasIslands)|(Ohio)|(Oklahoma)|(Oregon)|(Pennsylvania)|" +
                @"(PuertoRico)|(RhodeIsland)|(SouthCarolina)|(SouthDakota)|(Tennessee)|(Texas)|(Utah)|" +
                @"(Vermont)|(Virginia)|(VirginIslands)|(Washington)|(WestVirginia)|(Wisconsin)|(Wyoming))$";

            return inputstr.MatchRegex(strRegex, CaseSensitive);
        }

        public static bool IsEmpty(this string inputstr)
        {
            if (inputstr == null || inputstr.Trim().Length == 0)
            {
                return true;
            }
            return false;
        }

        public static bool IsPhone(this string inputstr)
        {
            // exit if the string is null or empty
            if (inputstr.IsEmpty())
                return false;

            string strRegex = @"^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$";
            return inputstr.MatchRegex(strRegex);
        }

        public static bool MatchRegex(this string inputstr, string regex)
        {
            return inputstr.MatchRegex(regex, false);
        }


        public static bool MatchRegex(this string inputstr, string regex, bool CaseSensitive)
        {
            var IsCaseSensitive = RegexOptions.IgnoreCase;
            if (CaseSensitive)
            {
                IsCaseSensitive = RegexOptions.None;
            }

            return System.Text.RegularExpressions.Regex.IsMatch(inputstr, regex, IsCaseSensitive);
        }

        public static bool EqualsIgnoreCase(this string s, string value)
        {
            return s.ToLowerInvariant() == value.ToLowerInvariant();
        }
        
        // be nice to VB & CF devlopers
        public static string Left(this string s, int position)
        {
            return s.Substring(0, position);
        }

        // be nice to VB & CF devlopers
        public static string Right(this string s, int position)
        {
            return s.Substring(s.Length - position);
        }

        public static string Reverse(this string s)
        {
            char[] charArray = s.ToCharArray();
            Array.Reverse(charArray);
            return new string(charArray);
        }

        public static string FormatTitleCase(this string s)
        {
            string[] words = s.Split(' ');

            string result = string.Empty;
            string temp = string.Empty;

            foreach (string i in words)
            {
                temp = i.ToLower();
                result = result + i.Substring(0, 1).ToUpper() + i.Substring(1) + " ";
            }

            result = result.Substring(0, result.Length - 1);

            return result;
        }

        /// <summary>
        /// convert a string that is CSV formated into a List
        /// credit goes to www.usualdosage.com where I got most of this code
        /// </summary>
        /// <param name="s"></param>
        /// <returns>List</returns>
        public static List<string> CSVToList(this string s)
        {
            char CHAR_COMMA = Convert.ToChar(44);
            char CHAR_QUOTE = Convert.ToChar(34);
            char CHAR_PIPE = Convert.ToChar(124);
            char CHAR_BACKQUOTE = Convert.ToChar(96);
            // Create a new array to contain the return values
       
            string strCurrent = s;
            string[] arrTokens = null;
            
            // Replace any instance of double quotes with a back quote
            strCurrent = strCurrent.Replace("\"\"", "`");
            
            if (!strCurrent.Contains(CHAR_QUOTE.ToString())) // No quotes, so just split on commas
            {
                arrTokens = strCurrent.Split(CHAR_COMMA);
            }
            else // Contains quotes, so more detailed parsing needed
            {
                bool bQuote = false;
                bool bEscape = false;
            
                // Convert temp to a char array to make it easier to check each char
                char[] arrTempChars = strCurrent.ToCharArray();
                for (int i = 0; i < arrTempChars.Length; i++)
                {
                    // Loop through until we find a quote. If found, flip the bool value
                    if (arrTempChars[i] == CHAR_QUOTE)
                    {
                        if (!bEscape)
                        bQuote = !bQuote;
                    }

                    // If we’re in a quote group and find a comma, change it to a temp char for the moment
                    if (bQuote && (arrTempChars[i] == CHAR_COMMA))
                    {
                        arrTempChars[i] = CHAR_PIPE;
                    }
                }
               // Parse out the modified character array into a string
               string strReassemble = string.Empty;
               
               for (int j = 0; j < arrTempChars.Length; j++)
               {
                   strReassemble = strReassemble + arrTempChars[j].ToString();
               }
               
               // Remove the quotes, which will leave just commas, and split
               strReassemble = strReassemble.Replace(CHAR_QUOTE.ToString(), string.Empty);
               arrTokens = strReassemble.Split(CHAR_COMMA);
               
               // Loop through the array, and reset the temp chars back to
               // commas, and replace the back quotes with double quotes
               for (int k = 0; k < arrTokens.Length; k++)
               {
                   arrTokens[k] = arrTokens[k].Replace(CHAR_PIPE, CHAR_COMMA);
                   arrTokens[k] = arrTokens[k].Replace(CHAR_BACKQUOTE, CHAR_QUOTE);
               }
            }

            List<string> rtrnList = new List<string>();
            foreach (string entry in arrTokens)
            {
                rtrnList.Add(entry);
            }

            return rtrnList;
        }

        /// <summary>
        /// convert a string that is CSV formated into a List
        /// credit goes to www.usualdosage.com where I got most of this code
        /// </summary>
        /// <param name="s"></param>
        /// <returns>List</returns>
        public static string[] CSVToArray(this string s)
        {
            return s.CSVToList().ToArray();
        }

        /// <summary>
        /// Convert a string[] into a single string that is seperated by a 
        /// delimiter
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string ToDelimitedString(this string[] s)
        {
            return s.ToDelimitedString(",", false);
        }

        public static string ToDelimitedString(this string[] s, string vDelimeter)
        {
            return s.ToDelimitedString(vDelimeter, false);
        }
        public static string ToDelimitedString(this string[] s, bool QuoteEachEntry)
        {
            return s.ToDelimitedString(",", QuoteEachEntry);
        }

        public static string ToDelimitedString(this string[] s, string Delimeter, bool QuoteEachEntry)
        {
            string result = string.Empty;
            string vDelim = string.Empty;

            foreach (string element in s)
            {
                if(QuoteEachEntry)
                    result += vDelim + "\"" + element + "\"";
                else
                    result += vDelim + element;
                vDelim = Delimeter;
            }
            return result;
        }
    }
}

Refactorings

No refactoring yet !

22e33503870d8e20493c4dd6b2f9767f

Rik Hemsley

August 6, 2008, August 06, 2008 19:26, permalink

No rating. Login to rate!

Quite a few of your new methods already exist in .NET.

For example:

IsEmpty(this string inputstr) already exists as string.IsNullOrEmpty(...)

ToDelimitedString(this string[] s) exists as string.Join(...)

Your email address 'validator' is incorrect. The only way to correctly validate an email address is according to RFC 2822. This cannot be done using a regular expression, only with a proper parser. It's almost always best to avoid trying to validate email addresses.

I'd also say that you're likely to slightly annoy other developers with so many tiny utility methods - especially where it's not clear exactly how they differ from the almost identical built-in methods.

For example: If I see 'inputstr.MatchRegex(strRegex)', I don't know whether that performs matches against the given regex, or whether it checks whether the string matches. The English is unclear.

Regex.IsMatch() from System.Text.RegularExpressions is clear in its purpose and I'd probably prefer to simply use it unadorned.

Your variable naming is slightly non-standard - please see Microsoft's guidelines for .NET code. For example, we don't use type prefixes in .NET, except for on interfaces (which have 'I').

I'm not entirely sure why you've made some of these methods extensions. What does a string have to do with the states of the USA or phone numbers? Left(), FormatTitleCase() etc. I can understand, as they deal with strings.

One last thing I would mention is that you didn't post your unit tests. It would be interesting to see them, to get an idea of how you intend to use your utility methods.

Hope this helps and isn't too negative!

95fd293467e8e89e44701d09eead51d4

snafu918

August 6, 2008, August 06, 2008 20:05, permalink

No rating. Login to rate!

Thanks Rik, I am new to .Net so any code review is useful. Thanks for the tip on Join and IsNullOrEmpty when you start a new language the function names are always different so I didn't even know they existed. I'll rework some of my naming so it's a bit more understandable I can see how some of them would be confusing. Some of those functions like left and FormatTitleCase are just formatting functions that I built as proof of concepts so I could get used to using .net.

Cd40128e044f39d7063b5cfdeace80f6

volothamp

August 8, 2008, August 08, 2008 13:09, permalink

No rating. Login to rate!

Some refactorings, didn't touch your csv algorithm.

Use always string buffer when you are concatenating strings.

The purpose of FormatTitleCase is unknown to me.

Bye! :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Utilities
{

    public static class StringExt
    {
        public static bool MatchRegexWithCheck(this string s, string pattern, bool casesensitive)
        {
            return !String.IsNullOrEmpty(s) && 
                (casesensitive ? Regex.Match(s, pattern) : Regex.Match(s, pattern, RegexOptions.IgnoreCase))
                .Success;
        }

        public static bool MatchRegexWithCheck(this string s, string pattern)
        {
            MatchRegexWithCheck(s, pattern, true);
        }

        public static bool IsEmail(this string inputstr)
        {
            return inputstr.MatchRegexWithCheck(@"^([a-zA-Z0-9_\-\.\+]+)@((\[[0-9]{1,3}" +
                  @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
                  @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$");
        }

        public static bool IsZip(this string inputstr)
        {
            return inputstr.MatchRegexWithCheck(@"^\d{5}$|^\d{5}-\d{4}$");
        }

        public static bool IsStateAbbrv(this string inputstr, bool casesensitive)
        {
            return inputstr.MatchRegexWithCheck(@"^((AL)|(AK)|(AS)|(AZ)|(AR)|(CA)|(CO)|(CT)|(DE)|(DC)|(FM)|(FL)|" +
                @"(GA)|(GU)|(HI)|(ID)|(IL)|(IN)|(IA)|(KS)|(KY)|(LA)|(ME)|(MH)|(MD)|(MA)|(MI)|" +
                @"(MN)|(MS)|(MO)|(MT)|(NE)|(NV)|(NH)|(NJ)|(NM)|(NY)|(NC)|(ND)|(MP)|(OH)|(OK)|" +
                @"(OR)|(PW)|(PA)|(PR)|(RI)|(SC)|(SD)|(TN)|(TX)|(UT)|(VT)|(VI)|(VA)|(WA)|(WV)|" +
                @"(WI)|(WY))$", casesensitive);
        }

        public static bool IsStateName(this string inputstr, bool casesensitive)
        {
            return inputstr.MatchRegex(@"^((Alabama)|(Alaska)|(AmericanSamoa)|(Arizona)|(Arkansas)|(California)|" +
                @"(Colorado)|(Connecticut)|(Delaware)|(DistrictofColumbia)|(Florida)|(Georgia)|(Guam)|" +
                @"(Hawaii)|(Idaho)|(Illinois)|(Indiana)|(Iowa)|(Kansas)|(Kentucky)|(Louisiana)|(Maine)|" +
                @"(Maryland)|(Massachusetts)|(Michigan)|(Minnesota)|(Mississippi)|(Missouri)|(Montana)|" +
                @"(Nebraska)|(Nevada)|(NewHampshire)|(NewJersey)|(NewMexico)|(NewYork)|(NorthCarolina)|" +
                @"(NorthDakota)|(NorthernMarianasIslands)|(Ohio)|(Oklahoma)|(Oregon)|(Pennsylvania)|" +
                @"(PuertoRico)|(RhodeIsland)|(SouthCarolina)|(SouthDakota)|(Tennessee)|(Texas)|(Utah)|" +
                @"(Vermont)|(Virginia)|(VirginIslands)|(Washington)|(WestVirginia)|(Wisconsin)|(Wyoming))$", casesensitive);
        }

        public static bool IsStateAbbrv(this string inputstr)
        {
            return inputstr.IsStateAbbrv(false);
        }

        public static bool IsStateName(this string inputstr)
        {
            return inputstr.IsStateName(false);
        }

        public static bool IsPhone(this string inputstr)
        {
            return inputstr.MatchRegexWithCheck(@"^\D?(\d{3})\D?\D?(\d{3})\D?(\d{4})$");
        }

        public static bool EqualsIgnoreCase(this string s, string value)
        {
            return s.ToLowerInvariant() == value.ToLowerInvariant();
        }

        // be nice to VB & CF devlopers
        public static string Left(this string s, int position)
        {
            return s.Substring(0, position);
        }

        public static string Right(this string s, int position)
        {
            return s.Substring(s.Length - position);
        }

        public static string Reverse(this string s)
        {
            char[] charArray = s.ToCharArray();
            Array.Reverse(charArray);
            return new string(charArray);
        }

        public static string FormatTitleCase(this string s)
        {
            string[] words = s.Split(' ');

            string result = string.Empty;
            string temp = string.Empty;

            foreach (string i in words)
            {
                temp = i.ToLower();
                // This get replaced every time
                result = String.Format("{0}{1}{2} ", result, i.Substring(0, 1).ToUpper(), i.Substring(1));
            }

            return result.Substring(0, result.Length - 1);
        }

        /// <summary>
        /// convert a string that is CSV formated into a List
        /// credit goes to www.usualdosage.com where I got most of this code
        /// </summary>
        /// <param name="s"></param>
        /// <returns>List</returns>
        public static List<string> CSVToList(this string s)
        {
            char CHAR_COMMA = ',';
            char CHAR_QUOTE = Convert.ToChar(34);
            char CHAR_PIPE = '|';
            char CHAR_BACKQUOTE = "`";

            string strCurrent = s;
            string[] arrTokens = null;

            // Replace any instance of double quotes with a back quote
            strCurrent = strCurrent.Replace("\"\"", CHAR_BACKQUOTE);

            if (!strCurrent.Contains(CHAR_QUOTE.ToString())) // No quotes, so just split on commas
            {
                arrTokens = strCurrent.Split(CHAR_COMMA);
            }
            else // Contains quotes, so more detailed parsing needed
            {
                bool bQuote = false;
                bool bEscape = false;

                // Convert temp to a char array to make it easier to check each char
                char[] arrTempChars = strCurrent.ToCharArray();
                for (int i = 0; i < arrTempChars.Length; i++)
                {
                    // Loop through until we find a quote. If found, flip the bool value
                    if (arrTempChars[i] == CHAR_QUOTE)
                    {
                        if (!bEscape)
                            bQuote = !bQuote;
                    }

                    // If we’re in a quote group and find a comma, change it to a temp char for the moment
                    if (bQuote && (arrTempChars[i] == CHAR_COMMA))
                    {
                        arrTempChars[i] = CHAR_PIPE;
                    }
                }
                // Parse out the modified character array into a string
                string strReassemble = string.Empty;

                for (int j = 0; j < arrTempChars.Length; j++)
                {
                    strReassemble = strReassemble + arrTempChars[j].ToString();
                }

                // Remove the quotes, which will leave just commas, and split
                strReassemble = strReassemble.Replace(CHAR_QUOTE.ToString(), string.Empty);
                arrTokens = strReassemble.Split(CHAR_COMMA);

                // Loop through the array, and reset the temp chars back to
                // commas, and replace the back quotes with double quotes
                for (int k = 0; k < arrTokens.Length; k++)
                {
                    arrTokens[k] = arrTokens[k].Replace(CHAR_PIPE, CHAR_COMMA);
                    arrTokens[k] = arrTokens[k].Replace(CHAR_BACKQUOTE, CHAR_QUOTE);
                }
            }

            List<string> rtrnList = new List<string>();
            foreach (string entry in arrTokens)
            {
                rtrnList.Add(entry);
            }

            return rtrnList;
        }

        /// <summary>
        /// convert a string that is CSV formated into a List
        /// credit goes to www.usualdosage.com where I got most of this code
        /// </summary>
        /// <param name="s"></param>
        /// <returns>List</returns>
        public static string[] CSVToArray(this string s)
        {
            return s.CSVToList().ToArray();
        }

        /// <summary>
        /// Convert a string[] into a single string that is seperated by a 
        /// delimiter
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string ToDelimitedString(this string[] s)
        {
            return s.ToDelimitedString(",", false);
        }

        public static string ToDelimitedString(this string[] s, string vDelimeter)
        {
            return s.ToDelimitedString(vDelimeter, false);
        }
        public static string ToDelimitedString(this string[] s, bool QuoteEachEntry)
        {
            return s.ToDelimitedString(",", QuoteEachEntry);
        }

        public static string ToDelimitedString(this string[] s, string delimeter, bool quoteEachEntry)
        {
            StringBuilder result = new StringBuilder();
            foreach (string element in s)
                result.AppendFormat(quoteEachEntry ? "{0}\"{1}\"" : "{0}{1}", delimeter, element);

            return result.ToString();
        }
    }
}
9cef2beb18f68b1b7fe18758a14f4e5a

John

October 17, 2008, October 17, 2008 17:02, permalink

No rating. Login to rate!

For e-mail address, state, zip, etc., you're really better off creating separate classes to represent each of those objects. Like, create an EmailAddress class that is able to validate itself.

6e73ef50bad8ec3b1b471700cbc644e5

Roe

October 17, 2008, October 17, 2008 17:20, permalink

No rating. Login to rate!

I agree that what you're suggesting is the better OO approach. What however would you suggest if you were going to refactor "THIS" code.

0e84aa0d95bd2275d48731b4beb31477

ermau

October 24, 2008, October 24, 2008 14:22, permalink

No rating. Login to rate!

Actually, String.IsNullOrEmpty does NOT trim before it checks length.

1
2
3
4
5
6
7
public static class StringExt
{
	public string IsEmpty (this string self)
	{
		return (self == null || self.Trim().Length == 0);
	}
}

Your refactoring





Format Copy from initial code

or Cancel