views:

67

answers:

4

Hi Guys..

I have this two objects

    class RNB
    {
        public RNB(double roomRate, double roomDays)
        {
            RoomRate = roomRate;
            RoomDays = roomDays;
        }

        public double RoomRate { get; set; }
        public double RoomDays { get; set; }
        public const double BasicLimit = 100;
    }

    class HMS
    {
        public double Amount { get; set; }
        public const double BasicLimit = 200;
    }

And then I have this method:

    public static double ComputeBasicAmount(double basicLimit, Func<double> multiplier)
    {
        return basicLimit * multiplier();
    }

Sample usage:

    static void Main(string[] args)
    {
        RNB rnb = new RNB(100, 2);
        double result = ComputeBasicAmount(RNB.BasicLimit, () => rnb.RoomDays * rnb.RoomRate);
        Console.WriteLine("RNB Basic Amt: " + result.ToString());

        HMS hms = new HMS() { Amount = 1000 };
        result = ComputeBasicAmount(HMS.BasicLimit, () => hms.Amount);
        Console.WriteLine("HMS Basic Amt: " + result.ToString());

        Console.Read();
    }

The problem here, I Want to eliminate the passing of the BasicLimit because i think it looks redundant here. Is it possible to put the BasicLimit inside the ComputeBasicAmount method

Something like this..

 public static double ComputeBasicAmount<T>(Func<T, double> multiplier, T obj)
  {

      return obj.BasicLimit * multiplier();
  }

Thanks in advance guys...

PS: The basicLimit doesn't have to be CONST

A: 

Yes you could, but you would have to check the type of obj first, since you can't restrict the type parameter to be of a specific class.

This means you can't define ComputeBasicAmount to only be called as ComputeBasicAmount<RNB> and ComputeBasicAmount<HMS>.

But, you could check if obj is either of the type RNB or HMS:

public static double ComputeBasicAmount<T>(Func<T, double> multiplier, T obj)
{
    var cObj1 = obj as RNB;
    if(cObj1!=null)
        return cObj1.BasicLimit * multiplier();

    var cObj2 = obj as HMS;
    if(cObj2 != null)
        return cObj1.BasicLimit * multiplier(); 

    return null; //Or throw an exception, e.g. argument exception
}
Giu
Meaning i have to use Reflection?
CSharpNoob
@CSharpNoob no, you could just check if `obj` is of type `RNB` and `HMS` using the `as` operator, or a cast, or something else. I've included a sample code
Giu
Does this means it will cast the obj even if its not really RNB, so if I have 100 objects, meaning the compiler will cast the object that im passing to 100 differnt object? thanks...
CSharpNoob
@CSharpNoob it will *try* to cast the object. If the cast fails, `as` will yield `null` rather than throwing an exception. Also, if you have 100 types, this method can get pretty ugly, since you would have to check against 100 types. In this case I would recommend you to use an `Attribute` as described in Jon Skeet's answer. My code snippet is only one suggestion of many on how you could approach the problem you described, but it's definitely not the most elegant one.
Giu
A: 

Well, all that this method does is multiple two double operands. There's already an operator for this. Can't you simply do this:

RNB rnb = new RNB(100, 2);
double result = RNB.BasicLimit * rnb.RoomDays * rnb.RoomRate;

HMS hms = new HMS() { Amount = 1000 };
result = HMS.BasicLimit * hms.Amount;
Darin Dimitrov
Its just an example, I'll plan to put some other stuff on the method as I will be evaluating different properties of different objects there...
CSharpNoob
Different properties and objects? Could you show exactly what you are trying to do as right now it seems a little vague?
Darin Dimitrov
A: 

No, you can't do it like that... even if you put in an interface, you can't specify that an instance method must be static, and then call it via T. You would have to use reflection, basically... and I think that's far uglier than specifying the room limit explicitly. You may want to read my proposal around static interfaces for a little more on this.

Essentially your two BasicLimit constants are unrelated as far as the compiler is concerned.

As you're already creating instances of the rooms, you could have an interface:

public interface IRoomType
{
    double BasicLimit { get; }
}

and then pass the instance of the room into the method, and ask it to fetch the limit that way. That's ugly because it's using an instance member to get at a value which isn't really specific to any particular instance, but that's the way of it.

If you are going to use reflection, you might want to consider specifying the limit in an attribute instead of a constant:

[BasicLimit(100)]
public class RNB { ... }

Then you could at least ask the type of T for the BasicLimitAttribute applied to it, if any. You wouldn't have compile-time safety that the attribute existed, but you would have safety against typos such as making one constant BascLimit instead of BasicLimit.

Jon Skeet
What about I use Interface and do Giu's suggestion? what do you think? thanks..
CSharpNoob
A: 

Perhaps a simpler answer here is to produce extension methods for the classes and just call ComputeBasicAmount without any parameters?

public static class ComputeBasicAmountEx
{
    public static double ComputeBasicAmount(this RNB rnb)
    {
        return RNB.BasicLimit * rnb.RoomDays * rnb.RoomRate;
    }

    public static double ComputeBasicAmount(this HMS hms)
    {
        return HMS.BasicLimit * hms.Amount;
    }
}

Your code then looks like this:

RNB rnb = new RNB(100, 2);
double result = rnb.ComputeBasicAmount();
Console.WriteLine("RNB Basic Amt: " + result.ToString());

HMS hms = new HMS() { Amount = 1000 };
result = hms.ComputeBasicAmount();
Console.WriteLine("HMS Basic Amt: " + result.ToString());

However, I don't think this is the best answer. I think I would prefer to create an interface and have the classes implement the interface, but the final code will still look the same.

Enigmativity
Our architetural design was, objects were just DTOs (Value Objects), im afraid I cant use extension methods on the DTOs and ruin the design.. :(, my plan is Implement Gui's suggestion and just create interface for the objects so that I'm sure that all the object that will be evaluated has BasicLimit property.. something like this public static double ComputeBasicAmount<T>(Func<T, double> multiplier, T obj) where T : IRoomType.. but I dont think its possible
CSharpNoob