The structure
public struct Tick : IEquatable<Tick>
{
public DateTime date;
public decimal price;
public int volume;
public Tick(DateTime date, decimal price, int volume)
{
this.date = date;
this.price = price;
this.volume = volume;
}
public override bool Equals(object obj)
{
var other = (Tick)obj;
return this.date == other.date && this.price == other.price && this.volume == other.volume;
}
public bool Equals(Tick other)
{
return this.date == other.date && this.price == other.price && this.volume == other.volume;
}
}
is changed in this test:
[Test]
public void MarshalDoesntRoundsDateTime() {
for (int i = 0; i < 1000; i++)
{
var now = new Tick(DateTime.Now.AddSeconds(i), i, i);
var now2 = now;
var ticks = new Tick[1];
unsafe
{
fixed (Tick* ptr = &ticks[0])
{
Marshal.StructureToPtr(now2, (IntPtr)ptr, false);
now2 = (Tick)Marshal.PtrToStructure((IntPtr)ptr, typeof(Tick));
Assert.AreEqual(now.date.Ticks, now2.date.Ticks);
}
}
}
}
Expected: 635719676058860752
But was: 635719676058860000
What is going on? Why DateTime is rounded after marshalling? Is this documented somewhere?
Marshal.StructureToPtr() is intended to marshal data for unmanaged code. There is are multiple "standards" for dates in native code, none that are close in range and accuracy to DateTime. The CLR designers went for the COM interop standard, also exposed by DateTime.ToOADate().
As you can tell from the Reference Source, it can be no more accurate than 1 msec. DateTime is accurate to 0.1 usec. Inevitably the last 4 digits you are looking at must be 0.
It is not clear why you are doing this or why it matters. Guessing, do keep in mind that Marshal.StructureToPtr() only seems like an attractive way to serialize .NET data.
The true error is that DateTime shouldn't be marshalable... If you try to Marshal it directly you get an ArgumentException.
If you really really want to Marshal a DateTime (and I don't even want to know why, considering it is a semi-proprietary format of .NET), you could:
public long date;
public DateTime Date
{
get
{
return DateTime.FromBinary(date);
}
set
{
date = value.ToBinary();
}
}
I have a large file which, in essence contains data like:
Netherlands,Noord-holland,Amsterdam,FooStreet,1,...,...
Netherlands,Noord-holland,Amsterdam,FooStreet,2,...,...
Netherlands,Noord-holland,Amsterdam,FooStreet,3,...,...
Netherlands,Noord-holland,Amsterdam,FooStreet,4,...,...
Netherlands,Noord-holland,Amsterdam,FooStreet,5,...,...
Netherlands,Noord-holland,Amsterdam,BarRoad,1,...,...
Netherlands,Noord-holland,Amsterdam,BarRoad,2,...,...
Netherlands,Noord-holland,Amsterdam,BarRoad,3,...,...
Netherlands,Noord-holland,Amsterdam,BarRoad,4,...,...
Netherlands,Noord-holland,Amstelveen,BazDrive,1,...,...
Netherlands,Noord-holland,Amstelveen,BazDrive,2,...,...
Netherlands,Noord-holland,Amstelveen,BazDrive,3,...,...
Netherlands,Zuid-holland,Rotterdam,LoremAve,1,...,...
Netherlands,Zuid-holland,Rotterdam,LoremAve,2,...,...
Netherlands,Zuid-holland,Rotterdam,LoremAve,3,...,...
...
This is a multi-gigabyte file. I have a class that reads this file and exposes these lines (records) as an IEnumerable<MyObject>. This MyObject has several properties (Country,Province,City, ...) etc.
As you can see there is a LOT of duplication of data. I want to keep exposing the underlying data as an IEnumerable<MyObject>. However, some other class might (and probably will) make some hierarchical view/structure of this data like:
Netherlands
Noord-holland
Amsterdam
FooStreet [1, 2, 3, 4, 5]
BarRoad [1, 2, 3, 4]
...
Amstelveen
BazDrive [1, 2, 3]
...
...
Zuid-holland
Rotterdam
LoremAve [1, 2, 3]
...
...
...
...
When reading this file, I do, essentially, this:
foreach (line in myfile) {
fields = line.split(",");
yield return new MyObject {
Country = fields[0],
Province = fields[1],
City = fields[2],
Street = fields[3],
//...other fields
};
}
Now, to the actual question at hand: I could use string.Intern() to intern the Country, Province, City, and Street strings (those are the main 'vilains', the MyObject has several other properties not relevant to the question).
foreach (line in myfile) {
fields = line.split(",");
yield return new MyObject {
Country = string.Intern(fields[0]),
Province = string.Intern(fields[1]),
City = string.Intern(fields[2]),
Street = string.Intern(fields[3]),
//...other fields
};
}
This will save about 42% of memory (tested and measured) when holding the entire dataset in memory since all duplicate strings will be a reference to the same string. Also, when creating the hierarchical structure with a lot of LINQ's .ToDictionary() method the keys (Country, Province etc.) of the resp. dictionaries will be much more efficient.
However, one of the drawbacks (aside a slight loss of performance, which is not problem) of using string.Intern() is that the strings won't be garbage collected anymore. But when I'm done with my data I do want all that stuff garbage collected (eventually).
I could use a Dictionary<string, string> to 'intern' this data but I don't like the "overhead" of having a key and value where I am, actually, only interested in the key. I could set the value to null or the use the same string as value (which will result in the same reference in key and value). It's only a small price of a few bytes to pay, but it's still a price.
Something like a HashSet<string> makes more sense to me. However, I cannot get a reference to a string in the HashSet; I can see if the HashSet contains a specific string, but not get a reference to that specific instance of the located string in the HashSet. I could implement my own HashSet for this, but I am wondering what other solutions you kind StackOverflowers may come up with.
Requirements:
My "FileReader" class needs to keep exposing an IEnumerable<MyObject>
My "FileReader" class may do stuff (like string.Intern()) to optimize memory usage
The MyObject class cannot change; I won't make a City class, Country class etc. and have MyObject expose those as properties instead of simple string properties
Goal is to be (more) memory efficient by de-duplicating most of the duplicate strings in Country, Province, City etc.; how this is achieved (e.g. string interning, internal hashset / collection / structure of something) is not important. However:
I know I can stuff the data in a database or use other solutions in such direction; I am not interested in these kind of solutions.
Speed is only of secondary concern; the quicker the better ofcourse but a (slight) loss in performance while reading/iterating the objects is no problem
Since this is a long-running process (as in: windows service running 24/7/365) that, occasionally, processes a bulk of this data I want the data to be garbage-collected when I'm done with it; string interning works great but will, in the long run, result in a huge string pool with lots of unused data
I would like any solutions to be "simple"; adding 15 classes with P/Invokes and inline assembly (exaggerated) is not worth the effort. Code maintainability is high on my list.
This is more of a 'theoretical' question; it's purely out of curiosity / interest that I'm asking. There is no "real" problem, but I can see that in similar situations this might be a problem to someone.
For example: I could do something like this:
public class StringInterningObject
{
private HashSet<string> _items;
public StringInterningObject()
{
_items = new HashSet<string>();
}
public string Add(string value)
{
if (_items.Add(value))
return value; //New item added; return value since it wasn't in the HashSet
//MEH... this will quickly go O(n)
return _items.First(i => i.Equals(value)); //Find (and return) actual item from the HashSet and return it
}
}
But with a large set of (to be de-duplicated) strings this will quickly bog down. I could have a peek at the reference source for HashSet or Dictionary or... and build a similar class that doesn't return bool for the Add() method but the actual string found in the internals/bucket.
The best I could come up with until now is something like:
public class StringInterningObject
{
private ConcurrentDictionary<string, string> _items;
public StringInterningObject()
{
_items = new ConcurrentDictionary<string, string>();
}
public string Add(string value)
{
return _items.AddOrUpdate(value, value, (v, i) => i);
}
}
Which has the "penalty" of having a Key and a Value where I'm actually only interested in the Key. Just a few bytes though, small price to pay. Coincidally this also yields 42% less memory usage; the same result as when using string.Intern() yields.
tolanj came up with System.Xml.NameTable:
public class StringInterningObject
{
private System.Xml.NameTable nt = new System.Xml.NameTable();
public string Add(string value)
{
return nt.Add(value);
}
}
(I removed the lock and string.Empty check (the latter since the NameTable already does that))
xanatos came up with a CachingEqualityComparer:
public class StringInterningObject
{
private class CachingEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public System.WeakReference X { get; private set; }
public System.WeakReference Y { get; private set; }
private readonly IEqualityComparer<T> Comparer;
public CachingEqualityComparer()
{
Comparer = EqualityComparer<T>.Default;
}
public CachingEqualityComparer(IEqualityComparer<T> comparer)
{
Comparer = comparer;
}
public bool Equals(T x, T y)
{
bool result = Comparer.Equals(x, y);
if (result)
{
X = new System.WeakReference(x);
Y = new System.WeakReference(y);
}
return result;
}
public int GetHashCode(T obj)
{
return Comparer.GetHashCode(obj);
}
public T Other(T one)
{
if (object.ReferenceEquals(one, null))
{
return null;
}
object x = X.Target;
object y = Y.Target;
if (x != null && y != null)
{
if (object.ReferenceEquals(one, x))
{
return (T)y;
}
else if (object.ReferenceEquals(one, y))
{
return (T)x;
}
}
return one;
}
}
private CachingEqualityComparer<string> _cmp;
private HashSet<string> _hs;
public StringInterningObject()
{
_cmp = new CachingEqualityComparer<string>();
_hs = new HashSet<string>(_cmp);
}
public string Add(string item)
{
if (!_hs.Add(item))
item = _cmp.Other(item);
return item;
}
}
(Modified slightly to "fit" my "Add() interface")
As per Henk Holterman's request:
public class StringInterningObject
{
private Dictionary<string, string> _items;
public StringInterningObject()
{
_items = new Dictionary<string, string>();
}
public string Add(string value)
{
string result;
if (!_items.TryGetValue(value, out result))
{
_items.Add(value, value);
return value;
}
return result;
}
}
I'm just wondering if there's maybe a neater/better/cooler way to 'solve' my (not so much of an actual) problem. By now I have enough options I guess
Here are some numbers I came up with for some simple, short, preliminary tests:
Non optimizedMemory: ~4,5GbLoad time: ~52s
StringInterningObject (see above, the ConcurrentDictionary variant)Memory: ~2,6GbLoad time: ~49s
string.Intern()Memory: ~2,3GbLoad time: ~45s
System.Xml.NameTableMemory: ~2,3GbLoad time: ~41s
CachingEqualityComparerMemory: ~2,3GbLoad time: ~58s
StringInterningObject (see above, the (non-concurrent) Dictionary variant) as per Henk Holterman's request:Memory: ~2,3GbLoad time: ~39s
Although the numbers aren't very definitive, it seems that the many memory-allocations for the non-optimized version actually slow down more than using either string.Intern() or the above StringInterningObjects which results in (slightly) longer load times. Also, string.Intern() seems to 'win' from StringInterningObject but not by a large margin; << See updates.
I've had exactly this requirement and indeed asked on SO, but with nothing like the detail of your question, no useful responses. One option that is built in is a (System.Xml).NameTable, which is basically a string atomization object, which is what you are looking for, we had (we've actually move to Intern because we do keep these strings for App-life).
if (name == null) return null;
if (name == "") return string.Empty;
lock (m_nameTable)
{
return m_nameTable.Add(name);
}
on a private NameTable
http://referencesource.microsoft.com/#System.Xml/System/Xml/NameTable.cs,c71b9d3a7bc2d2af shows its implemented as a Simple hashtable, ie only storing one reference per string.
Downside? is its completely string specific. If you do cross-test for memory / speed I'd be interested to see the results. We were already using System.Xml heavily, might of course not seem so natural if you where not.
When in doubt, cheat! :-)
public class CachingEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public T X { get; private set; }
public T Y { get; private set; }
public IEqualityComparer<T> DefaultComparer = EqualityComparer<T>.Default;
public bool Equals(T x, T y)
{
bool result = DefaultComparer.Equals(x, y);
if (result)
{
X = x;
Y = y;
}
return result;
}
public int GetHashCode(T obj)
{
return DefaultComparer.GetHashCode(obj);
}
public T Other(T one)
{
if (object.ReferenceEquals(one, X))
{
return Y;
}
if (object.ReferenceEquals(one, Y))
{
return X;
}
throw new ArgumentException("one");
}
public void Reset()
{
X = default(T);
Y = default(T);
}
}
Example of use:
var comparer = new CachingEqualityComparer<string>();
var hs = new HashSet<string>(comparer);
string str = "Hello";
string st1 = str.Substring(2);
hs.Add(st1);
string st2 = str.Substring(2);
// st1 and st2 are distinct strings!
if (object.ReferenceEquals(st1, st2))
{
throw new Exception();
}
comparer.Reset();
if (hs.Contains(st2))
{
string cached = comparer.Other(st2);
Console.WriteLine("Found!");
// cached is st1
if (!object.ReferenceEquals(cached, st1))
{
throw new Exception();
}
}
I've created an equality comparer that "caches" the last Equal terms it analyzed :-)
Everything could then be encapsulated in a subclass of HashSet<T>
/// <summary>
/// An HashSet<T;gt; that, thorough a clever use of an internal
/// comparer, can have a AddOrGet and a TryGet
/// </summary>
/// <typeparam name="T"></typeparam>
public class HashSetEx<T> : HashSet<T> where T : class
{
public HashSetEx()
: base(new CachingEqualityComparer<T>())
{
}
public HashSetEx(IEqualityComparer<T> comparer)
: base(new CachingEqualityComparer<T>(comparer))
{
}
public T AddOrGet(T item)
{
if (!Add(item))
{
var comparer = (CachingEqualityComparer<T>)Comparer;
item = comparer.Other(item);
}
return item;
}
public bool TryGet(T item, out T item2)
{
if (Contains(item))
{
var comparer = (CachingEqualityComparer<T>)Comparer;
item2 = comparer.Other(item);
return true;
}
item2 = default(T);
return false;
}
private class CachingEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public WeakReference X { get; private set; }
public WeakReference Y { get; private set; }
private readonly IEqualityComparer<T> Comparer;
public CachingEqualityComparer()
{
Comparer = EqualityComparer<T>.Default;
}
public CachingEqualityComparer(IEqualityComparer<T> comparer)
{
Comparer = comparer;
}
public bool Equals(T x, T y)
{
bool result = Comparer.Equals(x, y);
if (result)
{
X = new WeakReference(x);
Y = new WeakReference(y);
}
return result;
}
public int GetHashCode(T obj)
{
return Comparer.GetHashCode(obj);
}
public T Other(T one)
{
if (object.ReferenceEquals(one, null))
{
return null;
}
object x = X.Target;
object y = Y.Target;
if (x != null && y != null)
{
if (object.ReferenceEquals(one, x))
{
return (T)y;
}
else if (object.ReferenceEquals(one, y))
{
return (T)x;
}
}
return one;
}
}
}
Note the use of WeakReference so that there aren't useless references to objects that could prevent garbage collection.
Example of use:
var hs = new HashSetEx<string>();
string str = "Hello";
string st1 = str.Substring(2);
hs.Add(st1);
string st2 = str.Substring(2);
// st1 and st2 are distinct strings!
if (object.ReferenceEquals(st1, st2))
{
throw new Exception();
}
string stFinal = hs.AddOrGet(st2);
if (!object.ReferenceEquals(stFinal, st1))
{
throw new Exception();
}
string stFinal2;
bool result = hs.TryGet(st1, out stFinal2);
if (!object.ReferenceEquals(stFinal2, st1))
{
throw new Exception();
}
if (!result)
{
throw new Exception();
}
edit3:
instead of indexing strings, putting them in non-duplicate lists will save much more ram.
we have int indexes in class MyObjectOptimized. access is instant.
if list is short(like 1000 item) speed of setting values wont be noticable.
i assumed every string will have 5 character .
this will reduce memory usage
percentage : 110 byte /16byte = 9x gain
total : 5gb/9 = 0.7 gb + sizeof(Country_li , Province_li etc )
with int16 index (will further halve ram usage )
*note:* int16 capacity is -32768 to +32767 ,
make sure your list is not bigger than 32 767
usage is same but will use the class MyObjectOptimized
main()
{
// you can use same code
foreach (line in myfile) {
fields = line.split(",");
yield
return
new MyObjectOptimized {
Country = fields[0],
Province = fields[1],
City = fields[2],
Street = fields[3],
//...other fields
};
}
}
required classes
// single string size : 18 bytes (empty string size) + 2 bytes per char allocated
//1 class instance ram cost : 4 * (18 + 2* charCount )
// ie charcounts are at least 5
// cost: 4*(18+2*5) = 110 byte
class MyObject
{
string Country ;
string Province ;
string City ;
string Street ;
}
public static class Exts
{
public static int AddDistinct_and_GetIndex(this List<string> list ,string value)
{
if( !list.Contains(value) ) {
list.Add(value);
}
return list.IndexOf(value);
}
}
// 1 class instance ram cost : 4*4 byte = 16 byte
class MyObjectOptimized
{
//those int's could be int16 depends on your distinct item counts
int Country_index ;
int Province_index ;
int City_index ;
int Street_index ;
// manuallly implemented properties will not increase memory size
// whereas field WILL increase
public string Country{
get {return Country_li[Country_index]; }
set { Country_index = Country_li.AddDistinct_and_GetIndex(value); }
}
public string Province{
get {return Province_li[Province_index]; }
set { Province_index = Province_li.AddDistinct_and_GetIndex(value); }
}
public string City{
get {return City_li[City_index]; }
set { City_index = City_li.AddDistinct_and_GetIndex(value); }
}
public string Street{
get {return Street_li[Street_index]; }
set { Street_index = Street_li.AddDistinct_and_GetIndex(value); }
}
//beware they are static.
static List<string> Country_li ;
static List<string> Province_li ;
static List<string> City_li ;
static List<string> Street_li ;
}
I wish to create an online quiz that can ask any question from thousands of programmed questions. Each question is created via a function that is given an array of int whose values determine the exact question displayed. I have each question as a class:
public class AddingTwoDigitNumbers : IQuestion
{
public string QName() { return "Adding Two-Digit Numbers" };
public int[] QParams() { return int[]() {Random(10, 99), Random(10, 99) };
public void Question(int[] values) {
Console.WriteLine(string.Format("What is {1} + {2}?", values[0], values[1]);
}
public void Answer(int[] values) {
Console.WriteLine(values[0] + values[1]).ToString());
}
}
QParams creates the array of int (to determine exactly the question created), that is given to both Question and Answer to create the question and answer.
I want a List of questions searchable by QName but would rather not have to create (and name) thousands of classes all implementing IQuestion.
So here is my second solution:
public class Question
{
public string QName { get; set; }
public Func<int[]> QParams { get; set; }
public Action<int[]> Question { get; set; }
public Action<int[]> Answer { get; set; }
}
public class QuestionRepository
{
public static Dictionary<string, Question> Questions = new Dictionary<string, Question>();
public static void AddQuestions(Question[] qs) {
foreach (Question q in qs) Questions.Add(q.QName, q);
}
}
public class FirstSetOfQuestions
{
static void AddQuestions()
{
QuestionRepository.AddQuestions(new Question[]
{
new Question()
{
QName = "Adding Two-Digit Numbers",
QParams = () => int[]() {Random(10, 99), Random(10, 99) },
Question = (v) => {Console.WriteLine(string.Format("What is {1} + {2}?", v[0], v[1]);},
Answer = (v) => {Console.WriteLine(values[0] + values[1]).ToString());}
},
new Question()
{
QName = "Subtracting Three-Digit Numbers",
QParams = () => int[]() {Random(100, 999), Random(100, 999) },
Question = (v) => {Console.WriteLine(string.Format("What is {1} - {2}?", v[0], v[1]);},
Answer = (v) => {Console.WriteLine(values[0] - values[1]).ToString());}
}
}
}
}
So my question is which is better? Do I create thousands of classes, having to provide a name for each one, or do I create thousands of anonymous functions and a class that stores these using (I assume) delegates? Is there a problem with the second solution if I have thousands of questions, or even a better way to do this?
(Obviously the questions I wish to create are much more complicated than shown here, and involve fractions, algebra etc.)
Just to get you started with fluent syntax, throwing in some stubs and ideas in there as well.
class Question
{
public string Name { get; set; }
public string QuestionFormat { get; set; }
public List<Range> Args { get; set; }
public Expression<Func<int[], int>> ValExp { get; set; }
public Question(string name, string questionFormat)
{
this.Name = name;
this.QuestionFormat = questionFormat;
this.Args = new List<Range>();
}
public Question Rand(int min, int max)
{
this.Args.Add(new Range(min, max));
return this;
}
public void Val(Expression<Func<int[], int>> exp)
{
this.ValExp = exp;
}
public CompiledQuestion Compile()
{
// Generate args in the appropriate ranges
// Evaluate the result with the ValExp
// Return a new CompiledQuestion with the information -
// basically just replacing Args, ValExp with RealArgs, Val
}
public ICoolDataObject Save()
{
}
public static Question Load(ICoolDataObject hmm)
{
}
}
class Range
{
public int Min { get; set; }
public int Max { get; set; }
public Range(int min, int max)
{
this.Min = min;
this.Max = max;
}
}
It's almost fun, creating questions now:
new Question("simple addition",
"whats {0} + {1}?")
.Rand(10, 99)
.Rand(10, 99)
.Val(v => v[0] + v[1]);
You can obviously add some validation checks to avoid bad number of arguments due to late hours of work, and use double or decimal instead of int wherever.
Both approaches are wrong. I presume you are not going to have thousands of different types of calculations. You are only going to have a dozen or a few dozen different types of calculations, operating on a huge variety of data.
So, you need to normalize your data so as to end up with about a dozen or a few dozen different well defined calculations on a database of well defined data, end then write about a dozen or a few dozen classes, one for each kind of calculation, only.
You might think that this is too complicated, and you might think that writing thousands of classes (or delegates, it does not really matter) might be a lot of work but each piece is small and easy, but trust me, you will bitterly regret doing it this way as soon as something needs to change on the interface or the implementation of all of these classes, and most chances are that something will need to change at some point in time.
I have the following code in a Calculations.cs class:
public decimal decPaymentPlan(QuoteData quoteData)
{
if (quoteData.StepFilingInformation.PaymentPlanRadioButton
== StepFilingInformation.PaymentPlan.No)
return PriceQuote.priceNoPaymentPlan;
else
return PriceQuote.pricePaymentPlanChapter7; //may want to switch
//to Chapter13 value
}
public decimal CalculateChapter7(QuoteData quoteData)
{
decimal total = PriceQuote.priceChapter7;
total += this.decPaymentPlan(quoteData); //want to be able to tell
//which to use, 7 or 13
return total;
}
I am trying to see if I can avoid an extra decPaymentPlan where the final return is pricePaymentPlanChapter13. I thought there might be a way to switch it out.
Otherwise, I'd have to do the following:
public decimal decPaymentPlanChapter7(QuoteData quoteData)
{
...
else
return PriceQuote.pricePaymentPlanChapter7;
}
public decimal decPaymentPlanChapter13(QuoteData quoteData)
{
...
else
return PriceQuote.pricePaymentPlanChapter13;
}
...
//the following will appear anyway, but rather than just using
//one method call which switches the choice based on something
public decimal CalculateChpater7(QuoteData quoteData)
{
...
//instead of decPaymentPlan(quoteData) + something to switch
total+= this.decPaymentPlanChapter7(quoteData);
...
}
public decimal CalculateChpater13(QuoteData quoteData)
{
...
//instead of decPaymentPlan(quoteData) + something to switch
total+= this.decPaymentPlanChapter13(quoteData);
...
}
Is something like this doable (and how)? Thanks. Appreciate any code samples or guidance.
UPDATE:
This is my controller:
public ActionResult EMailQuote()
{
Calculations calc = new Calculations();
QuoteData quoteData = new QuoteData
{
StepFilingInformation = new Models.StepFilingInformation
{
//just moking user input here temporarily to test out the UI
PaymentPlanRadioButton = Models.StepFilingInformation.PaymentPlan.Yes,
}
};
var total = calc.CalculatePrice(quoteData);
ViewBag.CalculatePrice = total; // ADDED THIS LINE
return View(quoteData);
}
Also, I set a value in PriceQuote for Chapter7 and Chapter 13 (e.g., public static decimal priceChapter7 { get { return 799; } }
Hard to be sure of a suggestion without understanding more about what you are doing, but if the only difference between your methods are a set of values to use (one set for chapter7, the other for chapter13) it may make sense to take these values out of PriceQuote and create a base type to hold these values. Then your decPaymentPlan and other methods would only take an instance of that type. For example:
class Chapter // for lack of a better name
{
public decimal PaymentPlan { get; set; }
public decimal Price { get; set; }
....
}
Then, change your methods to take a Chapter parameter
public decimal decPaymentPlan(QuoteData quoteData, Chapter chapter)
{
if (quoteData.StepFilingInformation.PaymentPlanRadioButton
== StepFilingInformation.PaymentPlan.No)
return PriceQuote.priceNoPaymentPlan;
else
return chapter.PaymentPlan;
}
public decimal Calculate(QuoteData quoteData, Chapter chapter)
{
decimal total = chapter.Price;
total += this.decPaymentPlan(quoteData, chapter);
return total;
}
Now all you would need are two instances of Chapter, one for 7 and the other for 13, and call your calculate method accordingly.
UPDATE: To elaborate a bit on what I mean by 'call your calculate method accordingly', lets say for example you had two static variables (somewhere that makes sense in your application, perhaps in Calculations.cs)
static Chapter Chapter7 = new Chapter() { Price = 799.99, PaymentPlan = 555.55 };
static Chapter Chapter13 = ...
Then in your controller, you would be able to write
ViewBag.Chapter7Total = calc.CalculatePrice(quoteData, Chapter7);
ViewBag.Chapter13Total = calc.CalculatePrice(quoteData, Chapter13);
What's the difference between 7 and 13? I would just opt into doing:
if (quoteData.StepFilingInformation.PaymentPlanRadioButton ==
StepFilingInformation.PaymentPlan.No)
return PriceQuote.priceNoPaymentPlan;
else if (//whatever fulfills ch. 7)
return PriceQuote.pricePaymentPlanChapter7;
else //ch. 13
return PriceQuote.pricePaymentPlanChapter13;
It looks like you could create an Enumeration of the Chapters and pass that in as a second parameter to the decPaymentPlan method yes?
You are mixing your business logic with your visualization layer:
if (quoteData.StepFilingInformation.PaymentPlanRadioButton
== StepFilingInformation.PaymentPlan.No)
A better design would be to have a model on which changes are applied e.g. MVC, MVP, MVVM.
Example:
public class View
{
private Model _model = new Model();
public View()
{
}
public Controller Controller
{
get;
set;
}
private void OnButton1Click(object sender, EventArgs args)
{
_model.Option = Options.Option1;
}
private void OnSaveClick(object sender, EventArgs args)
{
if (Controller != null)
Controller.ApplyChanges(_model);
}
}
The controller can then apply business logic free of the view structure, so that you can change either of the two freely.
E.g.
public class Controller
{
Model Model
{
get;
set;
}
decimal CalculateSum()
{
return Model.Items.Aggregate((a, b) => a + b);
}
}
using (read = new StreamReader("C:/Users/Sam Smith/Desktop/convert.txt"))
{
while (!read.EndOfStream)
{
lineFromFile = read.ReadLine();
units = lineFromFile.Split(',');
if (units.Contains(splitEntry[0]) && units.Contains(splitEntry[1]))
{
firstUnit = units[0];
secondUnit = units[1];
userConvertValue = Convert.ToDouble(splitEntry[2]);
fileConvertValue = Convert.ToDouble(units[2]);
result = fileConvertValue * userConvertValue;
}
if (units.Contains(splitEntry[0]) && units.Contains(splitEntry[1]))
{
firstUnit = units[1];
secondUnit = units[0];
userConvertValue = Convert.ToDouble(splitEntry[2]);
fileConvertValue = Convert.ToDouble(units[2]);
result = userConvertValue / fileConvertValue;
}
if (!units.Contains(splitEntry[0]) || !units.Contains(splitEntry[1]))
{
Console.WriteLine("Error, measurement unit not recognised.");
}
Above I have a text file that contains types of unit measurement (pounds, ounces, miles and such), the text from this file is split into a string array.
The user enters two measurement units in the following format to convert to two units:
unit,unit,amount
In the text file, the conversion amount for two units is every third split string, like so:
unit,unit,2.80
unit,unit,1.27 (etc)
Is there a way of grouping each set of units and their conversion amounts? For example, if the user tries to convert two particular units, the program knows which conversion value to use when calculating the final result.
Might be a little vague, but it's difficult to explain.
EDIT: The user does not interact with the file, the program simply pulls the data from the file, which is then split into strings (',') and stored in an array.
If I don't got you wrong, the following code should fulfill your requirements (it's very basic, no error handling etc.):
public enum Unit
{
Pound,
Kilo,
Kilometer,
Mile
}
public class UnitMapping
{
public UnitMapping(Unit source, Unit target, double factor)
{
SourceUnit = source;
TargetUnit = target;
Factor = factor;
}
public Unit SourceUnit { get; private set; }
public Unit TargetUnit { get; private set; }
public double Factor { get; private set; }
}
public class UnitCalculator
{
public const string FILE_INPUT = #"Kilo,Pound,0.45359237
Kilometer,Mile,1.609344";
private List<UnitMapping> mappings;
public UnitCalculator()
{
this.mappings = new List<UnitMapping>();
// parse the mappings
foreach (var line in FILE_INPUT.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
{
var fields = line.Split(',');
var source = (Unit)Enum.Parse(typeof(Unit), fields[0]);
var target = (Unit)Enum.Parse(typeof(Unit), fields[1]);
double factor = double.Parse(fields[2], CultureInfo.InvariantCulture);
this.mappings.Add(new UnitMapping(source, target, factor));
}
}
public double Convert(Unit source, Unit target, double value)
{
foreach (var mapping in this.mappings)
{
if (mapping.SourceUnit == source && mapping.TargetUnit == target)
{
return value * mapping.Factor;
}
else if (mapping.SourceUnit == target && mapping.TargetUnit == source)
{
return value * (1 / mapping.Factor);
}
}
throw new InvalidOperationException("No mapping could be found for this conversion.");
}
}
Invoke it like this:
static void Main(string[] args)
{
var calc = new UnitCalculator();
Console.WriteLine(calc.Convert(Unit.Mile, Unit.Kilometer, 1));
}
If you don't know the units, you can use strings as well.