I am working in C#, winforms application.
I am reading from a text file where each row has fields divided by tabs:
I am putting each row in a list named tic_string. From here I am trying to search each list object, find the tabs, and put each field in its own array. So there will be an array for column a, column b, column c ... etc.
The problem is when I try to find the tabs in my list objects, it finds nothing. Here is my code:
string[] tic_num = new string[row_counter];
string[] tic_title = new string[row_counter];
string[] tic_owner = new string[row_counter];
string[] tic_open_date = new string[row_counter];
int last_tab = 0;
int char_counter = 0;
int feild_counter = 1;
int feild_char_count = 1;
int current_row=0;
string temp_feild = "";
char temp_char;
char tab_char = '\t';
foreach (string tic_string_value in tic_string)
{
temp_char = tic_string_value[char_counter];
if (temp_char == tab_char)
{
Console.WriteLine("tab_found");
if (feild_char_count == 1)
{
temp_feild = "";
}
else
{
temp_feild = tic_string_value.Substring(last_tab, feild_char_count);
}
last_tab = char_counter;
feild_char_count = 0;
switch (feild_counter)
{
case 1:
tic_num[current_row] = temp_feild;
break;
case 2:
tic_title[current_row] = temp_feild;
break;
case 3:
tic_owner[current_row] = temp_feild;
break;
case 4:
tic_open_date[current_row] = temp_feild;
break;
}
}
current_row++;
feild_char_count++;
char_counter++;
if (feild_counter == 5)
feild_counter = 1;
}
Your code seems to be too complicated for such simple task. Do not parse each line char by char, just use helper functions like String.Split etc.:
foreach (string tic_string_value in tic_string)
{
var parts = tic_string_value.Split(new [] { '\t' },
StringSplitOptions.RemoveEmptyEntries);
tic_num[current_row] = parts[0];
tic_title[current_row] = parts[1];
tic_owner[current_row] = parts[2];
tic_open_date[current_row] = parts[3];
current_row++;
}
First of all, I deduce from the style of your code that you are probably familiar with C/C++ and are new to C#, because this code has a particularly "C++" flavour to it. It reminds me very much of my own C# code when I first made the jump myself.
I am glad that you described the problem you are trying to solve rather than simply posting the code and asking where to find the bug because I think you can actually solve your problem much more simply.
Considering the following code (this assumes that you're iterating over each of the rows outside this code, and I omit some of the declaring of variables that you had already specified):
int field_counter = 0;
foreach (var field in tic_string.Split('\t')) {
switch (field_counter++) {
case 0:
tic_num[current_row] = field;
break;
case 1:
tic_title[current_row] = field;
break;
case 2:
tic_owner[current_row] = field;
break;
case 3:
tic_open_date[current_row] = field;
break;
}
}
This leverages the succinctness of C# and removes quite a few lines of code, which is always good. The String.Split method will take care of most of the string splitting for you, so there's no need to do it all manually and keep track of characters.
Note: I kept your original naming of some of the field names, although generally it is preferable to use CamelCase in C# code.
Now I notice from your original code that it's possible you don't have "rows" in your data in an actual sense (i.e. split by newline characters) but rather you may have the data entirely tab separated and are using the fact that you have a fixed number of columns per row to split up rows.
If this was the case, might I suggest the following code block could help you:
int i = 0;
foreach (var group in tic_string.GroupBy(x => i++ % 4)) {
int current_row = 0;
foreach (var field in group) {
switch (group.Key) {
case 0:
tic_num[current_row] = field;
break;
case 1:
tic_title[current_row] = field;
break;
case 2:
tic_owner[current_row] = field;
break;
case 3:
tic_open_date[current_row] = field;
break;
}
current_row++;
}
}
Now of course you may need to adapt these blocks to your code rather than use it verbatim. I hope that they at least demonstrate a different way of thinking about the problem. In particular, learning to use the various LINQ extension methods and LINQ queries will also be very helpful - they are part of what allows C# code to be so quick and easy to develop.
Best of luck in solving your problem!
You could also use a list instead of 4 string arrays:
public class ObjectToBeUsed
{
public Person(int num, string title, string owner, string opendate)
{
this.Num = num;
this.Title = title;
this.Owner = owner;
this.OpenDate = opendate;
}
private int _num;
public int Num
{
get { return _num; }
set { _num = value; }
}
private string _title;
public string Title
{
get { return _title; }
set { _title = value; }
}
private string _owner;
public string Owner
{
get { return _owner; }
set { _owner = value; }
}
private string _opendate;
public string OpenDate
{
get { return _opendate; }
set { _opendate = value; }
}
}
This is the class which describes each row in your text file.
System.IO.StreamReader file = new System.IO.StreamReader("test.txt");
string currentLine = null;
List<ObjectToBeUsed> peopleList = new List<ObjectToBeUsed>();
while ((currentLine = file.ReadLine()) != null)
{
string[] tokens = Regex.Split(currentLine, #"\t");
peopleList.Add(new ObjectToBeUsed(Convert.ToInt32(tokens[0]), tokens[1], tokens[2], tokens[3]));
}
The code is pretty self-explanatory, but if you need any further explaining, go ahead.
Related
I hope all of you are having a nice day. So I fixed one error of my program but there's another :/
So here's the code where I create my and read the data from a file:
void ReadData(string fileName, Branch[] branches)
{
string shopsName = null;
using (StreamReader reader = new StreamReader(#fileName))
{
string line = null;
line = reader.ReadLine();
if (line != null)
{
shopsName = line;
}
Branch tempBranches = TempBranch(branches, shopsName);
string address = reader.ReadLine();
string phoneNumber = reader.ReadLine();
while (null != (line = reader.ReadLine()))
{
string[] values = line.Split(';');
string facturer = values[0];
string model = values[1];
double capacity = double.Parse(values[2]);
string energyClass = values[3];
string assemblyType = values[4];
string color = values[5];
string attribute = values[6];
double cost = double.Parse(values[7]);
Fridges fridge = new Fridges(facturer, model, capacity, energyClass, assemblyType, color, attribute, cost);
tempBranches.fridges.AddFridge(fridge);
}
}
And there's the code where I use the TempBranch method. The error is in this line: if (branches[i].ShopsName == shopsName). Hopefully you can help me, cuz I was trying to fix this yesterday for 30 minutes and it still wasn't working :D
private static Branch TempBranch(Branch[] branches, string shopsName)
{
for (int i = 0; i < MaxNumberOfFridges; i++)
{
if (branches[i].ShopsName == shopsName)
{
return branches[i];
}
}
return null;
}
If you replace MaxNumberOfFridges with branches.Length it will only try to find a Branch that's within the range of the branches array. The reason it's not working is because you're trying to access an index which is greater than the Length of the array.
Try this one. Use foreach, if you dont know the lenght of array.
private static Branch TempBranch(Branch[] branches, string shopsName)
{
foreach(var branch in branches)
{
if (branch.ShopsName == shopsName)
{
return branch;
}
}
return null;
}
You can also try to make use of a LINQ query,
return branches.Where(b => b.ShopsName == shopsName).FirstOrDefault();
EDIT:
To NullReferenceError which occurs in your new post occurs due to null being returned in your function where your shop gets created. This due to not finding the given shopname.
So it tries to add an fridge to an shop which does not exist, which is not possible. You will have to add a check so that this does not occur.
This raised error because MaxNumberOfFridges is bigger than branches length.. to simplify it, assume MaxNumberOfFridges is 20 but arry length is 10, so you are trying to access element 11 in array which is outside of array length.
to fix it
for (int i = 0; i < branches.Length; i++)
{
if (branches[i].ShopsName == shopsName)
{
return branches[i];
}
}
other option is to use foreach loop
foreach(var b in branches)
{
if (b.ShopsName == shopsName)
{
return branches[i];
}
}
I've seen the articles on StackOverflow regarding c++ long switch statements, but c# is different in this regard.
Specifically, I would like to replace a hugely-long switch statement in c# code, where each case statement does the same thing, just the field name changes.
The code looks like this:
case Fields.TRANSMITTERCONTACTPHONEEXT:
{
int lengthNeeded = 0;
int currentLength = TransmitterContactTelephoneExtension.Length;
int lengthRequired = TransmitterContactTelephoneExtensionLength;
if (currentLength < lengthRequired)
{
lengthNeeded = lengthRequired - currentLength;
for (int i = 0; i < lengthNeeded; i++)
{
TransmitterContactTelephoneExtension += " ";
}
}
} break;
case Fields.TRANSMITTERFEIN:
{
int lengthNeeded = 0;
int currentLength = TransmitterFEIN.Length;
int lengthRequired = TransmitterFEINLength;
if (currentLength < lengthRequired)
{
lengthNeeded = lengthRequired - currentLength;
for (int i = 0; i < lengthNeeded; i++)
{
TransmitterFEIN += " ";
}
}
} break;
I'd like to just get this down to a single function that can figure out which field I mean without having to use a switch statement. Is there a way to pass in a variable containing the field name?
Place all the changeable values into arrays and index into it. Make sure the enum int values are the same as the data in the target arrays.
var current = (int) Fields.TRANSMITTERCONTACTPHONEEXT;
int lengthNeeded = 0;
int currentLength = LengthData[ current ] ;
int lengthRequired = RequiredData[current ];
if (currentLength < lengthRequired)
{
lengthNeeded = lengthRequired - currentLength;
for (int i = 0; i < lengthNeeded; i++)
{
Extensions[ current ] = Extensions[ current ] + " ";
}
}
This post looks at the pattern of the operations in the switch and does not address any localized deficiencies of individual operations. Please look at each of the individual operations and where needed improve it for optimal operational efficiencies.
This just looks like the code for a string pad function repeated multiple times. You could just have
case Fields.TRANSMITTERCONTACTPHONEEXT:
TransmitterContactTelephoneExtension = TransmitterContactTelephoneExtension.PadRight(TransmitterContactTelephoneExtensionLength, ' ');
break;
...
Any time that you find yourself repeating code over and over, you probably can break it out into a separate function (if one does not already exist) and just call it with the right parameters.
And this also makes me wonder if you need the switch case statement at all, and not just a series of pad statements. But that is going further out (in scope) in your code. And your post does not give us enough info to go there.
Finally, somewhat applicable to your question, my rule of thumb (not originally mine, but I forget where I got it) is that if a method is more than a pageful (intentionally vague term), then I need to break it up into other separate methods. That allows me to look at a method and understand it without scrolling around. It also forces me to separate a longer process into smaller logical steps.
You really should break this out into a method. So that you are not duplicating code over and over. That is inefficient and can lead to potential errors when updating.
Also instead of looping you should make use of the PadRight() method on strings.
I would do this:
case Fields.TRANSMITTERCONTACTPHONEEXT:
TransmitterContactTelephoneExtension = PadString(TransmitterContactTelephoneExtensionLength, TransmitterContactTelephoneExtension);
break;
case Fields.TRANSMITTERFEIN:
TransmitterFEIN = PadString(TransmitterFEINLength, TransmitterFEIN);
break;
private string PadString(int requiredLen, string value)
{
if (value == null) return String.Empty.PadRight(requiredLen, ' '); //Create an empty string when the value is null
return value.PadRight(requiredLen, ' ');
}
Declare the variables in advance, and use the switch only for the assignments that differ:
int currentLength;
int lengthRequired;
switch (whatever) {
case Fields.TRANSMITTERCONTACTPHONEEXT:
currentLength = TransmitterContactTelephoneExtension.Length;
lengthRequired = TransmitterContactTelephoneExtensionLength;
break;
case Fields.TRANSMITTERFEIN:
currentLength = TransmitterFEIN.Length;
lengthRequired = TransmitterFEINLength;
break;
default:
throw new Exception(); // Without this, the compiler will complain about uninitialized variables
}
int lengthNeeded = 0;
if (currentLength < lengthRequired)
{
lengthNeeded = lengthRequired - currentLength;
for (int i = 0; i < lengthNeeded; i++)
{
TransmitterFEIN += " ";
}
}
switch (whatever) {
case Fields.TRANSMITTERCONTACTPHONEEXT:
TransmitterContactTelephoneExtension += " ";
break;
case Fields.TRANSMITTERFEIN:
TransmitterFEIN += " ";
break;
}
Edit: OmegaMan's solution is way better if you have the option of replacing the variables with an array.
To me it seems like the code we can't see needs some refactoring, but based on what we can see, I would recommend doing the following:
// 1. Have a class to hold your data
class FieldData
{
public string Value { get; set; }
public int LengthRequired { get; set; }
public string RightPaddedValue
{
get { return Value.PadRight(LengthRequired, ' '); }
}
}
// 2. Fill your data into a dictionary somehow... for example:
Dictionary<Fields, FieldData> fields = new Dictionary<Fields, FieldData>
{
{
Fields.TRANSMITTERCONTACTPHONEEXT,
new FieldData {
Value = TransmitterContactTelephoneExtension,
LengthRequired = TransmitterContactTelephoneExtensionLength
}
},
{
Fields.TRANSMITTERFEIN,
new FieldData {
Value = TransmitterFEIN,
LengthRequired = TransmitterFEINLength
}
}
};
// 3. Then use the data from that dictionary in your code:
FieldData data = fields[selectedField];
data.RightPaddedValue; // use RightPaddedValue
Here is my situation every time we add a new product to our catalog we need to go through add the product to this snippet and recompile the dll's, then push them out to all of the servers.
What I'd like to be able to do is to be able to just add a line to a text file that the DLL then reads in. To make things easy the file will reside in the same folder as the DLL's. The main issue is there are several places that call this all of which pass in the productId, so changing that would not be feasible.
void AssignFunctions(int productId)
{
switch (productId)
{
case 31:
IsSpread = CalendarIsSpread;
IsLeftPage = CalendarIsLeftPage;
GetOppositePageNumber = GeneralGetOppositePageNumber;
break;
case 49:
case 63:
case 64:
case 69:
case 70:
...
case 592:
case 630:
case 686:
IsSpread = NeverASpread;
IsLeftPage = GeneralIsLeftPage;
GetOppositePageNumber = GeneralGetOppositePageNumber;
break;
case 73:
IsSpread = GeneralIsSpread;
IsLeftPage = GeneralIsLeftPage;
GetOppositePageNumber = GeneralGetOppositePageNumber;
break;
case 444:
case 445:
IsSpread = BookletIsSpread;
IsLeftPage = BookletLeftPage;
GetOppositePageNumber = BookletGetOppositePageNumber;
break;
default:
IsSpread = GeneralIsSpread;
IsLeftPage = GeneralIsLeftPage;
GetOppositePageNumber = GeneralGetOppositePageNumber;
break;
}
}
Another scenario would be to take the productId and compare it to the text file and then act accordingly. In that case the call would look something like this:
void AssignFunctions(int productId)
{
//Do Something here to get the productSpreadType
switch (productSpreadType)
{
case 1:
IsSpread = CalendarIsSpread;
IsLeftPage = CalendarIsLeftPage;
GetOppositePageNumber = GeneralGetOppositePageNumber;
break;
case 2:
IsSpread = NeverASpread;
IsLeftPage = GeneralIsLeftPage;
GetOppositePageNumber = GeneralGetOppositePageNumber;
break;
case 3:
IsSpread = GeneralIsSpread;
IsLeftPage = GeneralIsLeftPage;
GetOppositePageNumber = GeneralGetOppositePageNumber;
break;
case 4:
IsSpread = BookletIsSpread;
IsLeftPage = BookletLeftPage;
GetOppositePageNumber = BookletGetOppositePageNumber;
break;
default:
IsSpread = GeneralIsSpread;
IsLeftPage = GeneralIsLeftPage;
GetOppositePageNumber = GeneralGetOppositePageNumber;
break;
}
}
In this case the text file would look something like this:
31 1
49 2
63 2
...
73 3
444 4
If the productId is not listed it would just then perform the default action.
.NET is not my forte and it takes a good half day every time we add new products to remember how to update this. As I am the only one with Visual Studio running on Windows the task always fall to me. With the exception of having to make this change we don't have to touch the code base, so it would be nice to not have to build the DLL's every time and be able to pass the task of making sure this gets updated to one of our junior developers, who make are the ones responsible for adding new products.
C# is a static language and is not meant for dynamic code like you're suggesting.
I would however consider a different design where you can read different categories from a database and act on them as categories rather than individual items with the same handling.
Looks like you can group a lot of these together.
You can also consider a config file as the source for the categories.
You could use a little reflection magic:
using System;
using System.Linq;
using System.Reflection;
public class Program
{
public static void Main()
{
// Test Data
DoProduct(31);
DoProduct(63);
DoProduct(49);
DoProduct(49);
DoProduct(61);
}
public static void DoProduct(int productId)
{
string data = "";
bool methodExecuted = false;
// I am loading the string "data" with test data for simplicity.
// In real life, you'll load this from your file intead.
data = data + "31 MyFirstFunction" + Environment.NewLine;
data = data + "49 MySecondFunction" + Environment.NewLine;
data = data + "63 MyThirdFunction" + Environment.NewLine;
foreach (string str in data.Replace(Environment.NewLine, "\n").Replace('\r', '\n').Split('\n'))
{
if (string.IsNullOrEmpty(str))
continue;
int pid = int.Parse(str.Split(' ')[0]);
string func = str.Split(' ')[1];
if (pid != productId)
continue;
Type type = typeof(Program);
MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public);
MethodInfo method = methods.FirstOrDefault(z => z.Name.Equals(func));
if (method == null) {
continue;
}
method.Invoke(null, null);
methodExecuted = true;
}
if (!methodExecuted)
{
MyDefaultFunction();
}
}
public static void MyFirstFunction()
{
Console.WriteLine("This is MyFirstFunction()!");
}
public static void MySecondFunction()
{
Console.WriteLine("This is MySecondFunction()!");
}
public static void MyThirdFunction()
{
Console.WriteLine("This is MyThirdFunction()!");
}
public static void MyDefaultFunction()
{
Console.WriteLine("This is MyDefaultFunction()!");
}
}
This will allow you to create a text file, like this:
31 MyFunction
63 AnotherFunction
...
And using reflection, it will execute the correct function every time you pass in a product id. If the product id is not in the text file, it will call the default function.
Here is a fiddle of it in action: https://dotnetfiddle.net/7Atpq7
If you really must use a text file you could load it into a multiple value dictionary using a struct, then perform a search on that dictionary
assuming test data of the following in a text file called test.txt:
1,value1,value2
2,value1,value2
...
you could do something like this:
class Program
{
// declare test data
public static int[] _products = new int[] { 1, 2, 3, 4, 5 };
// Searchable value dictionary to be filled by file
static Dictionary<int,Values> d = new Dictionary<int,Values>();
static void Main(string[] args)
{
// Read the file line by line into the dictionary
string line;
System.IO.StreamReader file =
new System.IO.StreamReader("c:\\test\\test.txt");
while ((line = file.ReadLine()) != null)
{
string[] values = line.Split(',');
int productId = int.Parse(values[0]);
Values v = new Values();
v.Value1 = values[1];
v.Value2 = values[2];
d.Add(productId, v);
}
file.Close();
// check our loaded config file
foreach (int p in _products)
{
if (d.Keys.Contains(p))
{
// we've found a match in the file
// your logic goes here!
}
else
{
// no match was found
// handle missing data
}
}
}
// struct to hold your data from text file
struct Values
{
public string Value1;
public string Value2;
}
}
I have a large list and I would like to overwrite one value if required. To do this, I create two subsets of the list which seems to give me an OutOfMemoryException. Here is my code snippet:
if (ownRG != "")
{
List<string> maclist = ownRG.Split(',').ToList();
List<IVFile> temp = powlist.Where(a => maclist.Contains(a.Machine)).ToList();
powlist = powlist.Where(a => !maclist.Contains(a.Machine)).ToList(); // OOME Here
temp.ForEach(a => { a.ReportingGroup = ownRG; });
powlist.AddRange(temp);
}
Essentially I'm splitting the list into the part that needs updating and the part that doesn't, then I perform the update and put the list back together. This works fine for smaller lists, but breaks with an OutOfMemoryException on the third row within the if for a large list. Can I make this more efficient?
NOTE
powlist is the large list (>1m) items. maclist only has between 1 and 10 but even with 1 item this breaks.
Solving your issue
Here is how to rearrange your code using the enumerator code from my answer:
if (!string.IsNullOrEmpty(ownRG))
{
var maclist = new CommaSeparatedStringEnumerable(str);
var temp = powlist.Where(a => maclist.Contains(a.Machine));
foreach (var p in temp)
{
p.ReportingGroup = ownRG;
}
}
You should not use ToList in your code.
You don't need to remove thee contents of temp from powlist (you are re-adding them anyway)
Streaming over a large comma-separated string
You can iterate over the list manually instead of doing what you do now, by looking for , characters and remembering the position of the last found one and the one before. This will definitely make your app work because then it won't need to store the entire set in the memory at once.
Code example:
var str = "aaa,bbb,ccc";
var previousComma = -1;
var currentComma = 0;
for (; (currentComma = str.IndexOf(',', previousComma + 1)) != -1; previousComma = currentComma)
{
var currentItem = str.Substring(previousComma + 1, currentComma - previousComma - 1);
Console.WriteLine(currentItem);
}
var lastItem = str.Substring(previousComma + 1);
Console.WriteLine(lastItem);
Custom iterator
If you want to do it 'properly' in a fancy way, you can even write a custom enumerator:
public class CommaSeparatedStringEnumerator : IEnumerator<string>
{
int previousComma = -1;
int currentComma = -1;
string bigString = null;
bool atEnd = false;
public CommaSeparatedStringEnumerator(string s)
{
if (s == null)
throw new ArgumentNullException("s");
bigString = s;
this.Reset();
}
public string Current { get; private set; }
public void Dispose() { /* No need to do anything here */ }
object IEnumerator.Current { get { return this.Current; } }
public bool MoveNext()
{
if (atEnd)
return false;
atEnd = (currentComma = bigString.IndexOf(',', previousComma + 1)) == -1;
if (!atEnd)
Current = bigString.Substring(previousComma + 1, currentComma - previousComma - 1);
else
Current = bigString.Substring(previousComma + 1);
previousComma = currentComma;
return true;
}
public void Reset()
{
previousComma = -1;
currentComma = -1;
atEnd = false;
this.Current = null;
}
}
public class CommaSeparatedStringEnumerable : IEnumerable<string>
{
string bigString = null;
public CommaSeparatedStringEnumerable(string s)
{
if (s == null)
throw new ArgumentNullException("s");
bigString = s;
}
public IEnumerator<string> GetEnumerator()
{
return new CommaSeparatedStringEnumerator(bigString);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Then you can iterate over it like this:
var str = "aaa,bbb,ccc";
var enumerable = new CommaSeparatedStringEnumerable(str);
foreach (var item in enumerable)
{
Console.WriteLine(item);
}
Other thoughts
Can I make this more efficient?
Yes, you can. I suggest to either work with a more efficient data format (you can take a look around databases or XML, JSON, etc. depending on your needs). If you really want to work with comma-separated items, see my code examples above.
There's no need to create a bunch of sub-lists from powlist and reconstruct it. Simply loop over the powlist and update the ReportingGroup property accordingly.
var maclist = new HashSet<string>( ownRG.Split(',') );
foreach( var item in powlist) {
if( maclist.Contains( item.Machine ) ){
item.ReportingGroup = ownRG;
}
}
Since this changes powlist in place, you won't allocate any extra memory and shouldn't run into an OutOfMemoryException.
In a loop find the next ',' char. Take the substring between the ',' and the previous ',' position. At the end of the loop save a reference to the previous ',' position (which is initially set to 0). So you parse the items one-by-one rather than all at once.
You can try looping the items of your lists, but this will increase processing time.
foreach(var item in powlist)
{
//do your opeartions
}
I have recently started learning programming and chose .NET with Visual Studio Express. I am trying to write a CSV Parser as a learning experience and it's giving me a lot more trouble than I expected. I am starting with the reader. One thing I am doing differently in my parser is that I am not using quotes. I am escaping commas with a backslash, backslashes with a backslash, and line breaks with a backslash. For example, if a comma is preceded by an even number of backslashes it is a field and I halve any blocks of backslashes. If it's odd, it's not end of field and I still halve blocks of backslashes. I'm not sure how robust this will be if I can ever get it working, except I'm only learning at this point and I'm looking at it mostly as an exercise in manipulating data structures.
I have a question in reference to the code snippet at the bottom of this post and how to make it not so static and limiting and still compile and run for me.
The line of code that reads:
var contents = (String)fileContents;
I keep trying to make it more dynamic to increase flexibility and make it something like this:
var contents = (otherVariableThatCouldChangeTypeAtRuntime.GetType())fileContents;
Is there something I can do to get it to do this and still compile? Maybe something like Option Infer from VB.NET might help, except I can't find that.
Also, I have written this in VB.NET as well. It seems to me that VB.NET allows me a considerably more dynamic style than what I've posted below, such as not having to type var over and over again and not having to keep casting my index counting variable into an integer over and over again if I shut off Option Strict and Option Explicit as well as turn on Option Infer. For example, C# won't let me type something analogous to the following VB.NET code even though I know the methods and properties I will be calling at run-time will be there at run-time.
Dim contents As Object = returnObjectICantDetermineAtComplieTime()
contents.MethodIKnowWillBeThereAtRunTime()
Can I do these things in C#? Anyways, here's the code and thanks in advance for any responses.
public class Widget
{
public object ID { get; set; }
public object PartNumber { get; set; }
public object VendorID { get; set; }
public object TypeID { get; set; }
public object KeyMarkLoc { get; set; }
public Widget() { }
}
public object ReadFromFile(object source)
{
var fileContents = new FileService().GetFileContents(source);
object records = null;
if (fileContents == null)
return null;
var stringBuffer = "";
var contents = (String)fileContents;
while (contents.Length > 0 && contents != "\r\n")
{
for (object i = 0; (int)i < contents.Length; i=(int)i+1 )
{
object character = contents[(int)i];
if (!stringBuffer.EndsWith("\r\n"))
{
stringBuffer += character.ToString();
}
if (stringBuffer.EndsWith("\r\n"))
{
var bSlashes = getBackSlashes(stringBuffer.Substring(0, stringBuffer.Length - 4));
stringBuffer = stringBuffer.Substring(0, stringBuffer.Length - 4);
if ((int)bSlashes % 2 == 0)
{
break;
}
}
}
contents = contents.Substring(stringBuffer.Length+2);
records = records == null ? getIncrementedList(new List<object>(), getNextObject(getFields(stringBuffer))) : getIncrementedList((List<object>)records, getNextObject(getFields(stringBuffer)));
}
return records;
}
private Widget getNextRecord(object[] fields)
{
var personStudent = new Widget();
personStudent.ID = fields[0];
personStudent.PartNumber = fields[1];
personStudent.VendorID = fields[2];
personStudent.TypeID = fields[3];
personStudent.GridPath = fields[4];
return personStudent;
}
private object[] getFields(object buffer)
{
var fields = new object[5];
var intFieldCount = 0;
var fieldVal = "";
var blocks = buffer.ToString().Split(',');
foreach (var block in blocks)
{
var bSlashes = getBackSlashes(block);
var intRemoveCount = (int)bSlashes / 2;
if ((int)bSlashes % 2 == 0) // Delimiter
{
fieldVal += block.Substring(0, block.Length - intRemoveCount);
fields[intFieldCount] += fieldVal;
intFieldCount++;
fieldVal = "";
}
else // Part of Field
{
fieldVal += block.Substring(0, block.Length - intRemoveCount - 1) + ",";
}
}
return fields;
}
private object getBackSlashes(object block)
{
object bSlashes = block.ToString().Length == 0 ? new int?(0) : null;
for (object i = block.ToString().Length - 1; (int)i>-1; i=(int)i-1)
{
if (block.ToString()[(int)i] != '\\') return bSlashes = bSlashes == null ? 0 : bSlashes;
bSlashes = bSlashes == null ? 1 : (int)bSlashes + 1;
}
return bSlashes;
}
}
Here is the web service code.
[WebMethod]
public object GetFileContents(object source)
{
return File.ReadAllText(source.ToString());
}
Dim contents As Object = returnObjectICantDetermineAtComplieTime()
contents.MethodIKnowWillBeThereAtRunTime()
You can do this with the dynamic type.
See for more information: http://msdn.microsoft.com/en-us/library/dd264736.aspx