|
I'm still new, so I don't know if this is the best way to do what I am needing, and would like some feedback/advice.
I have a class that contains a double value. This double could represent a dollar value or it could represent a percentage value. Only one or the other, it will never have both. At runtime, I will need to be able to tell what type of value this is. Here is what I came up with, which appears to work, but if there is a better way, I would prefer that.
public class Rate
{
public double Value;
}
public class RateDollar : Rate {}
public class RatePercentage : Rate {}
public class TransactionLine
{
public Rate Amount;
}
myTransactionLine.Amount = new RateDollar();
if(myTransactionLine.Amount.GetType() == typeof(RateDollar))
if(myTransactionLine.Amount.GetType() == typeof(RatePercent))
For some reason, this seems like it is a clunky workaround. It also seems like it's possible for someone to create the value that is neither a RateDollar or RatePercent, and just as a plain Rate. Is there some way to prevent this, or is there a better way to accomplish what I am trying to do?
|
|
|
|
|
It doesn't make much sense to have a Rate class if all it does is contain a double. Is there more to it then that? Is the Rate used elsewhere? I'm gonna go out on a limb and say that TransactionLine should just have a double Rate property and a RateType enum property that says whether its a % or $ amount.
|
|
|
|
|
Thanks for the response! The enum seems like it would be a simpler way to handle this. Since the Rate may be used in multiple different classes, I think I should go with an enum in the Rate class.
public enum ENRateType { Dollar, Percent }
public class Rate
{
public ENRateType RateType;
public double Rate;
}
|
|
|
|
|
|
Thanks for responding!
I haven't really gotten to learning a lot about interfaces (or abstract classes), but I am thinking that will be the next topic I tackle.
The reason for this setup is because I am interacting with another software, and that software has one value that represents either a dollar value or a percentage. For example, this is how it would show a transaction:
Widget $12.00
Shipping $3.95
Subtotal $15.95
Tax 8.875%
Total $30.11
When I get this information from the program, they have a rate class that contains three members, an enum for the price type (dollar or percent), a double for the Dollar value, and a double for the Percent value. Because the line can only be a dollar or value and not both, one of the doubles will always be null.
I get the feeling (but I may be wrong) that they way they did their setup isn't the best. I will probably implement a Rate class that contains an enum and a double, which should work, but if there is a 'better' way to do this, I would prefer to learn about that type of implementation.
|
|
|
|
|
To me this information indicates you are "free" to change the Rate class in the program you get the information from: is that correct ?
The use of the phrases "They have," and "they did," and, "I am interacting with another software," is what gives me the idea you may not have freedom to change the Rate class.
thanks, Bill
"Everything we call real is made of things that cannot be regarded as real." Niels Bohr
|
|
|
|
|
Definitely would not use an interface here. Whats the point? Not everything needs to be an interface. Having 3 classes (or interfaces or whatever) just to hold a double is over engineering at its finest .
|
|
|
|
|
|
Collin Jasnoch wrote: Because he has not stated his reason for layers but is trying to use it. That is
the point.
He did. He wanted to be able to tell the difference between a % and a $ amount. In which case an enum (or even a bool would suffice).
Collin Jasnoch wrote: I have seen cases where an empty interface is even used. It is not over
engineering. It is creating a base API.
By what the OP was an
interface is exactly what should be used.
OP wanted to have a single double that held 2 different types of values and wanted to know a way to tell the difference between them. Using his GetType() solution required having 3 classes. The enum solution requires an enum & a single class.
I'm not against interfaces, I use them all the time. I don't do stuff just cuz I can, I do stuff when its appropriate. I could also wear a tuxedo to go to Burger King, but I don't cuz that's slightly overkill .
|
|
|
|
|
A quick and dirty solution;
public class Rate
{
public double Value;
public override string ToString ()
{
string typeName = GetType().Name;
if (typeName.StartsWith(typeof(Rate).Name))
typeName = typeName.Substring(typeof(Rate).Name.Length);
return typeName;
}
}
It'd be cleaner to introduce a new property than to abuse the ToString-method. Putting an enum in there that returns "Dollar" or "%" would require the base-class to have knowledge thereof.
..unless you introduce an abstract property that returns an object. Yes, that introduces an extra cast in the calling code, but that could easily be wrapped in a utility-method.
Bastard Programmer from Hell
if you can't read my code, try converting it here[^]
|
|
|
|
|
Thanks for the code! That would work as a temporary solution, I think!
It's sounding like I should really start looking into abstract classes/properties for this type of need! I haven't gotten that far yet in my learning, but it's going to be my next topic for sure!
|
|
|
|
|
hpjchobbes wrote: Thanks for the code! That would work as a temporary solution, I think!
You're welcome. PIEBald has an even neater solution btw
Bastard Programmer from Hell
if you can't read my code, try converting it here[^]
|
|
|
|
|
I'd make Rate abstract and maybe make it a struct or replace the field with a read-only property.
hpjchobbes wrote: if(myTransactionLine.Amount.GetType() == typeof(RateDollar))
if(myTransactionLine.Amount is RateDollar
|
|
|
|
|
Lots of the responses here seem to be WAY over thinking things. 3 classes to hold a double value? Really? It doesn't seem like there is going to be any code in any of those 3 classes, so it should just be a single class with an enum indicating what it is. You aren't going to get around type checking all over the place. If you give us more info on what you are actually doing with the rates, we could provide more help. If its just for formatting, well, you would still have a single class and just override the ToString() that switches on the enum and formats with a % or $. And actually, the $ version should use the system formatter which formats currency according to the local settings.
|
|
|
|
|
SledgeHammer01 wrote: so it should just be a single class with an enum
A boolean would be sufficient.
|
|
|
|
|
I agree that a boolean would work fine, but an enumeration would be a lot easier to read. What if by some chance he has his requirements change and needs to keep track of more than two types for the value? This whole thing could go to hell really quick then. Seems like a lot of over engineering going on here
"The clue train passed his station without stopping." - John Simmons / outlaw programmer
|
|
|
|
|
|
OP already explained what he wanted:
Quote: I have a class that contains a double value. This double could represent a dollar value or it could represent a percentage value. Only one or the other, it will never have both. At runtime, I will need to be able to tell what type of value this is. Here is what I came up with, which appears to work, but if there is a better way, I would prefer that.
He wanted a class that holds a double value and a way to determine the type of data. He thought the GetType() solution was clunky.
|
|
|
|
|
|
Collin Jasnoch wrote: That is not exactly what his post states
Looks to me that that is exactly what the OP said.
|
|
|
|
|
I would be more inclined to use the enum over abstract classes and interface for something like this. Unless there is a deeper level of programming that isn't being shown I would stick with simplicity.
This would be my interpetation of the object you are wanting also with a nice ToRateString() method so you can use the rate object to easily display your value in preformatted string.
Rate r = new Rate(ENRateType.Dollar, 5.58d);
public enum ENRateType { Dollar, Percent }
public class Rate
{
public Rate()
{
RateType = ENRateType.Dollar;
Value = 0d;
}
public Rate(ENRateType rateType, double value) : this()
{
RateType = rateType;
Value = value;
}
public ENRateType RateType {get; internal set;}
public double Value { get; internal set; }
public string ToRateString()
{
if (RateType == ENRateType.Dollar) return "$" + Value.ToString("0.00");
return Value.ToString("0.000") + "%";
}
}
|
|
|
|
|
Trak4Net wrote: stick with simplicity
Same here. At least try to keep things as simple as possible.
"The clue train passed his station without stopping." - John Simmons / outlaw programmer
|
|
|
|
|
From what I see there are several ways to do this:
1. Use class/interface inheritance
2. Use an enum
3. Use a boolean
It all depends on what you want, If you want good control over the different types of rates you should use class/interface inheritance, if the dollar rate should have different functionality than say a euro rate (convert between rates etc...)
Example:
interface Rate
{
double getValue();
}
class DollarRate : Rate
{
double rawval;
internal double getValue()
{
return rawval * 6;
}
}
class PercetageRate : Rate
{
double rawval;
internal double getValue()
{
return rawval;
}
}
class EuroRate : Rate
{
double rawval;
internal double getValue()
{
return rawval * 9;
}
}
void Main()
{
if(myTransactionLine.Amount is DollarRate)
{
}
else if(myTransactionLine.Amount is PercetageRate)
{
}
}
If you don't need this then just use the enum approach to save time and space.
I also came up with my own approach, I think it's the fastest one that allows for more than 2 rates (IE the bool method)
It works like the enum approach but instead of enums you use constants. (It's faster because constants are converted to a raw value at compile time)
Example:
static class Rates
{
public const byte DOLLAR = 0;
public const byte PERCENTAGE = 1;
public const byte EURO = 2;
}
class Rate
{
double value;
byte type;
internal double Value
{
get { return value; }
}
internal byte Type
{
get { return type; }
}
}
void Main()
{
if(myTransactionLine.Amount.Type == Rates.DOLLAR)
{
}
else if(myTransactionLine.Amount is Rates.PERCENTAGE)
{
}
}
Hope this helps! Sorry for long post...
|
|
|
|
|
As is often the case with newish people, I don't think you've actually asked the right question. What's your real requirement here? From reading the thread, it appears to be that you want a surcharge, either a percentage or a fixed amount, to be applied to a bill. What's the interface to that?
interface ISurcharge {
double GetAmount(Order order);
string Name { get; }
}
... or, if it's just on the amount, you can pass a number instead of an order. My guess, though, is different tax rates may apply to different items so you will eventually need to pass the order. Note the important thing here: the value of the surcharge depends (in general) on the existing order so it needs to be part of the interface. The Name property is for what gets displayed on the bill; you may want a few other little things like that, but this is the core functionality.
Now you can create trivial implementation classes to solve the problem!
class FixedSurcharge : ISurcharge {
private double amount;
public string Name { get; private set; }
public FixedSurcharge(string name, double amount) { Name = name; this.amount = amount; }
public double GetAmount(Order order) { return amount; }
}
class PercentageSurcharge : ISurcharge {
private double percentage;
public string Name { get; private set; }
public PercentageSurcharge (string name, double percentage) { Name = name; this.percentage = percentage; }
public double GetAmount(Order order) { return percentage * 0.01 * order.TotalValue; }
}
... called like:
void placeOrder(Order order){
foreach(OrderItem item in order.Items) { addBillItem(item.Name, item.Value); }
addSurcharge(new PercentageSurcharge("VAT", 20), order);
addSurcharge(new FixedSurcharge("Shipping", 2.50), order);
}
void addSurcharge(ISurcharge surcharge, Order order){
addBillItem(surcharge.Name, surcharge.GetAmount(order));
}
(Actually, on reflection it may be cleaner to make fake order items, with a Value property, by passing the existing Order to a constructor and stashing it in the Surcharge instance. That's a matter of style, I think.)
|
|
|
|
|
I have a DataGridView on a form which is populated through databinding from a table in my database. I've followed, more or less, the process outlined on this MSDN page[^]
I've implemented the databinding both ways so that, by clicking on an "Update" button, the changes that have been made on the DataGridView is stored back into the DB. All works well, but...
Imagine the table used for the databinding being something like this:
USERS
- UserID (INT)
- UserName (VARCHAR)
- UserType (INT)
And here's the problem, that UserType field references the identifier in a separate table like this one:
USERTYPES
- UserType (INT)
- UserTypeDescription (VARCHAR)
Currently, my databinding is done with a simple query like this:
SELECT * FROM USERS;
The problem is that the value displayed in the third column is an integer which means nothing to the end user. If I wasn't interested in two way databinding but only wanted to display the contents of the table in the DataGridView I could easily have overcome that problem by using the following query:
SELECT UserID, UserName, UserTypeDescription FROM USERS INNER JOIN USERTYPES ON USERS.UserType = USERTYPES.UserType;
Unfortunately I need for the end user to be able to change that value and I'd like to put a ComboBox in all the cells of that column which is populated from the USERTYPES table.
Would really appreciate some advice.
***EDIT***
Maybe I should add that I have some experience with adding a ComboBox (rather a DataGridViewComboBoxCell) in a DataGridView but that has always been to DataGridViews that have not been databound to a table.
Here's how I would've done that:
private void myDataGridView_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
for (int row = 0; row < (sender as DataGridView).Rows.Count; row++)
{
DataGridViewComboBoxCell newCell = new DataGridViewComboBoxCell();
using (SqlCommand sqlCommand = new SqlCommand("SELECT UserType, UserDescription FROM UserTypes", mySQLConnection))
{
using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader())
{
DataSet ds1 = new DataSet();
DataTable dt1 = new DataTable();
ds1.Tables.Add(dt1);
ds1.Load(sqlDataReader, LoadOption.PreserveChanges, ds1.Tables[0]);
newCell.DataSource = ds1.Tables[0];
newCell.DisplayMember = "GroupName";
newCell.ValueMember = "GroupID";
}
}
newCell.Value = myDataGridView.Rows[row].Cells["UserType"].Value;
myDataGridView.Rows[row].Cells["UserType"].Value = newCell;
}
}
That final line of code generates an ugly exception which I can only assume is because the DataGridView is bound to a table in the DB because in other projects I've used this code successfully.
The exception that I get is:
Unable to cast object of type 'System.Windows.Forms.DataGridViewComboBoxCell' to type 'System.IConvertible'.Couldn't store <DataGridViewComboBoxCell { ColumnIndex=-1, RowIndex=-1 }> in ConnectGroupID Column. Expected type is Int32.
modified 27-Jul-12 8:37am.
|
|
|
|
|