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.
Related
I have Sprache set up to parse an Equation that has a number of different possible method calls in it. After it resolves the method, is there a way to determine the index values within the original string? Perhaps the Parse has a "current index" value and "length" value that's somehow accessible?
Example input string:
IndexOf("fred", 2) + IndexOf("bob")
using a parser like this...
Parser<Expression> FunctionCall = from namePart in Parse.Letter.Many().Text()
from lparen in Parse.Char('(')
from expr in Parameter.DelimitedBy(ListDelimiter)
from rparen in Parse.Char(')')
select CallMethod(namePart, Enumerable.Repeat(sourceData, 1)
.Concat(expr)
.ToArray());
Can anyone think of a "trick" that would allow me to determine that the first CallMethod handles SubString(0, 18), and the second CallMethod handles SubString(21, 14) from the original string?
If you use a generic class and extension method you can make a more general approach
public class PositionAware<T> : IPositionAware<PositionAware<T>>
{
public PositionAware(T value)
{
Value = value;
}
public T Value { get; }
public Position Start { get; private set; }
public int Length { get; private set; }
public PositionAware<T> SetPos(Position startPos, int length)
{
Start = startPos;
Length = length;
return this;
}
}
public static Parser<PositionAware<T>> WithPosition<T>(this Parser<T> value)
{
return value.Select(x => new PositionAware<T>(x)).Positioned();
}
Using it:
from c in Parse.Char('a').WithPosition()
select (c.Start, c.Value)
from c in Parameter.DelimitedBy(ListDelimiter).WithPosition()
select (c.Start, c.Value)
I've managed to answer my own question. It's the Positioned() parser extension call that allows a parser to track the position within the original text.
Parser<Expression> FunctionCall = (from namePart in Parse.Letter.Many().Text()
from lparen in Parse.Char('(')
from expr in Parameter.DelimitedBy(ListDelimiter)
from rparen in Parse.Char(')')
select new MethodPosAware(namePart, expr)).Positioned()
.Select(x => CallMethod(x.Value, Enumerable.Repeat(sourceData, 1)
.Concat(x.Params)
.ToArray(),
x.Pos.Pos, x.Length));
I had to make a new MethodPosAware class to keep the position information, that derives from Sprache's IPositionAware:
class MethodPosAware : IPositionAware<MethodPosAware>
{
public MethodPosAware(string methodName, IEnumerable<Expression> parameters)
{
Value = methodName;
Params = parameters;
}
public MethodPosAware SetPos(Position startPos, int length)
{
Pos = startPos;
Length = length;
return this;
}
public Position Pos { get; set; }
public int Length { get; set; }
public string Value { get; set; }
public IEnumerable<Expression> Params { get; set; }
}
I think I'll be extending this further to work with more than just Method names, but this is sufficient to answer my question for now. I hope this helps someone down the road.
I have been given some code that has objects composed of lists of different types. A simple example of what I mean:
public class Account
{
private long accountID;
private List<string> accountHolders;
private List<string> phoneNumbers;
private List<string> addresses;
public Account()
{
this.accountHolders = new List<string>();
this.phoneNumbers = new List<string>();
this.addresses = new List<string>();
}
public long AccountID
{
get
{
return this.accountID;
}
set
{
this.accountID = value;
}
}
}
For a requirement I need to get the total amount of elements in each list for validation purposes. I have the following method which works:
public class AccountParser
{
// Some code
public int CountElements(Account acct)
{
int count = 0;
count += acct.accountHolders.Count();
count += acct.phoneNumbers.Count();
count += acct.addresses.Count();
return count;
}
}
but was wondering if there was a better way to do this. I know I can enumerate over a List with Linq but I can't seem to get it to work in this case.
What you're doing is the right thing
You could do it in one line without declaring any variable
public int CountElements(Account acct)
{
return acct.accountHolders.Count() + acct.phoneNumbers.Count() + acct.addresses.Count();
}
But it doesn't change much.
The ammount of lists is static, because the class is static, so it doesn't make sense to use Reflection if the structure wont change.
Now you could have more than one Account classes with different types of lists. In that case, i would create an abstract AbsAccount class, that has an abstract CountElements property:
public abstract class AbsAccount
{
public abstract int CountElements { get; }
}
public class Account: AbsAccount
{
private List<string> accountHolders;
private List<string> phoneNumbers;
private List<string> addresses;
public override int CountElements
{
get
{
return this.accountHolders.Count()
+ this.phoneNumbers.Count()
+ this.addresses.Count();
}
}
}
public class AccountParser
{
// Some code
public int CountElements(AbsAccount acct)
{
return acct.CountElements;
}
}
But maybe im taking it too far...
You can add items to a list then call .Summethod on it, but it's not better from performance point of view.
public class AccountParser
{
// Some code
public int CountElements(Account acct)
{
List<string> all = new List<string>();
all.AddRange(acct.accountHolders);
all.AddRange(acct.phoneNumbers);
all.AddRange(acct.addresses);
return all.Count();
}
}
Another approach will be (because I can see you are not exposing directly your lists) to use observer pattern, and update the number of elements in another field or even list, every time you are updating one of your lists. Then get the value from that field, but I think the best way is the one you have already adopted.
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);
}
}
I have the following class:
public class VendorClass {
public int VendorID { get; set; }
public string VendorName { get; set; }
}
The fields above match fields in the database table.
In the case of say VendorName, how do I give it a field width ?
VendorName maps to a field in the database which is varchar(15)
You can't limit the length of the string but you can use properties with backing fields to achieve the desired result :
public class VendorClass
{
public int VendorID { get; set; }
private string _vendorName;
public string VendorName
{
get { return _vendorName; }
set
{
if (value.Length > 15)
{
_vendorName = value.Substring(0,15);
} else {
_vendorName = value;
}
}
}
}
Strings in C# have almost-arbitrary length.
When loading from your database, it will automatically accommodate the actual string length. When saving to the database, your business logic, data layer or ORM (as appropriate) will need to ensure the proper maximum length.
A string can't have a set length in C#. You will have to handle the db length through some other mechanism like validation. Can't really tell you more without more details.
I would question why you would do this in c# code. However this link has a couple of ways around this. I suppose either truncation or taking a subsring is the best option. You could also make sure that the UI (or the model-view) takes care of details such as this.
I am not sure exactly what you are asking, but if you want to know the maximum length of a string, this question can help you.
If you want to limit the number of characters entered, I would suggest that you use server-side validation and/or client-side validation.
I just met a problem like what you described and found a way to create a limited length's string. Maybe a little inflexible but concise when there are only finite varchar length definitions in database.
Firstly introduce some basic classes:
public class Length16
{
public static int StringLength { get => 16; }
}
public class Length8
{
public static int StringLength { get => 8; }
}
public class Length15
{
public static int StringLength { get => 15; }
}
public class LimitedLengthString<T>
{
private string _sValue;
public LimitedLengthString(string sNewValue)
{
_sValue = sNewValue;
}
public static implicit operator LimitedLengthString<T>(string sNewValue)
{
var prop = typeof(T).GetProperty("StringLength");
int iLength = (int)prop.GetValue(null);
if (sNewValue.Length > iLength)
{
throw new Exception($"New string is too long! Allowed length {iLength}.");
}
return new LimitedLengthString<T>(sNewValue);
}
public static implicit operator string(LimitedLengthString<T> sSource)
{
return sSource.ToString();
}
public override string ToString()
{
return _sValue;
}
}
public class AutoTruncatedString<T>
{
private string _sValue;
public AutoTruncatedString(string sNewValue)
{
_sValue = sNewValue;
}
public static implicit operator AutoTruncatedString<T>(string sNewValue)
{
var prop = typeof(T).GetProperty("StringLength");
int iLength = (int)prop.GetValue(null);
return new AutoTruncatedString<T>(sNewValue.Substring(0, iLength));
}
public static implicit operator string(AutoTruncatedString<T> sSource)
{
return sSource.ToString();
}
public override string ToString()
{
return _sValue;
}
}
Use them like this:
LimitedLengthString<Length8> sLimitedLength8;
sLimitedLength8 = "asdfgasdfg"; // will error out
AutoTruncatedString<Length8> sAutoTruncated8;
sAutoTruncated8 = "asdfgasdfg"; // will be truncated
sLimitedLength8 will throw an error if you try to assign a string longer than 8 and sAutoTruncated8 will truncate the string you assign to it.
For you, you can define the VendorName this way:
public LimitedLengthString<Length15> VendorName { get; set; }
Hope this could help you.
I am fairly new to arrays in C# and am used to storing a mass of data in a string and in INI files and then breaking it down into basic arrays using delimiters...so yeh, my knowledge is almost none existent.
My main form class begin this definition:
public CAirportData[] _AirportData; //size not known
This is the method I am using to create the array:
...string[] airports = possibleAirports.Split(','); //size is known
foreach (string airport in airports)
{
string[] rwys = inif.Read(airport, "rwys").Split(':'); //size is known (2)
_AirportData = new CAirportData[] { new CAirportData() { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] } };
}
I know this just boils down to my limited knowledge of objects and arrays. But I can't seem to find anything on the internet that uses this sort of thing. I have tried to combine other peoples code with little success.
I need the _AirportData array to be available outside of the form hence public and declared outside of any methods. I supose the main problem is that I am overwriting array and foreach airport I am creating a new array hence loosing the previous. I had tried moving the ..= new CAirportData[] to all sorts of places but Visual Studio doesn't like it.
Below is the class definition for CAirportData:
public class CAirportData
{
public string icao { get; set; }
public string depRwy { get; set; }
public string arrRwy { get; set; }
public override string ToString()
{
string result = string.Format("ICAO: {0}, Dep: {1}, Arr: {2}", this.icao, this.depRwy, this.arrRwy);
return result;
}
}
public class CMRunways
{
public string icao { get; set; }
public string depRwy { get; set; }
public string arrRwy { get; set; }
}
Many thanks in advance for any help!
What you're looking for is generic List. Change the definition to:
public List<CAirportData> _AirportData = new List<CAirportData>();
Then the code in the loop to:
_AirportData.Add(new CAirportData { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] });
This is what I would do...Create a static class, with a static property (airports) and add a static constructor to load the airports from file at the begining.
public static class Session
{
public static CAirportData[] _AirportData;
static Session()
{
string airports = possibleAirports.Split(",");
foreach (string airport in airports)
{
string[] rwys = inif.Read(airport, "rwys").Split(':'); //size is known (2)
_AirportData = new CAirportData[] { new CAirportData() { icao=airport, depRwy=rwys[0], arrRwy=rwys[1] } };
}
}
}
Now you can access the array anywhere in the project like
MessageBox.Show(Session.CAirportData[0].depRwy);