开发者

How to return multiple values from a function in C# (ASP.NET)?

I have a page where some products and textfields where the user enters a number. I first use JavaScript to calculate the total cost. Based on how many users they enter they get a different rate (as shown in code below). When the user types or paste a number into a textfield the function CalculateCost gets called which calls the other functions (only showing two in the example, CDCOst and DVDCost) to make sure the fields Monthly Cost and Annual Cost are displaying the correct value.

I of course want to do the final calculation in the code behind as well before I insert into the database. How can I achieve something similiar in C#?

function CDCost() {
                var monthlyAmount;
                var annualAmount;
                var amount;
                var users = $('#txtCD').val();

            if (users > 0 && users < 100) {
                amount = users * 14.95;
                monthlyAmount = amount;
                annualAmount = monthlyAmount * 12;
                return [monthlyAmount, annualAmount];
            }
            if (users >= 100 && users <= 250) {
                amount = users * 12.95;
                monthlyAmount = amount;
                annualAmount = monthlyAmount * 12;
                return [monthlyAmount, annualAmount];
            }
            if (users == 0) {
                monthlyAmount = 0;
                annualAmount = 0;
                return [monthlyAmount, annualAmount];
            }
        }

function DVDCost() {
                var monthlyAmount;
                var annualAmount;
                var amount;
                var users = $('#txtDVD').val();

            if (users > 0 && users < 100) {
                amount = users * 16.95;
                monthlyAmount = amount;
                annualAmount = monthlyAmount * 12;
                return [monthlyAmount, annualAmount];
            }
            if (users >= 100 && users <= 250) {
                amount = users * 14.95;
                monthlyAmount = amount;
                annualAmount = monthlyAmount * 12;
                return [monthlyAmount, annualAmount];
            }
            if (users == 0) {
                monthlyAmount = 0;
                annualAmount = 0;
                return [monthlyAmount, annualAmount];
            }
        }


        function CalculateCo开发者_开发百科st() {
            var cd = CDCost();
            var dvd = DVDCost();

            var monthlyCost = cd[0] + dvd[0];
            var annualCost = cd[1] + dvd[1];

            return [monthlyCost, annualCost];
        }

        $('#txtCD').bind('keyup change', function (ev) {
            var cost = CalculateCost();
            var monthly = cost[0];
            var annual = cost[1];

            $('#MonthlyCostSum').text(monthly);
            $('#AnnualCostSum').text(annual)
        });

How would I go on doing this in C#?

Something like:

protected double CDCost()
    {
        double monthlyAmount;
        double annualAmount;
        double amount;
        double users = Convert.ToDouble(txtCD.Text);

        if (users > 0 && users < 100)
    {
        amount = users * 14.95;
        monthlyAmount = amount;
        annualAmount = monthlyAmount * 12;
        return //how do I return the values here to a CalculateCost function?
    }


}


Either use out parameters or create a new type wrapping all the things you would like to return (a so-called "property bag"):

class ReportData
{
    public double MonthlyAmount { get; set; }
    public double AnnualAmount { get; set; }
    public double Amount { get; set; }
}

...

protected ReportData CDCost()
{
    return new ReportData() 
        { 
            Amount = users * 14.95
            MonthlyAmount = amount, 
            AnnualAmount = amount * 12.0,
        };
}


The tuple class in .NET 4 does the job perfectly.

protected Tuple<double, double, double, double> CDCost()
    {
        double monthlyAmount;
        double annualAmount;
        double amount;
        double users = Convert.ToDouble(txtCD.Text);
   if (users > 0 && users < 100)
    {
        amount = users * 14.95;
        monthlyAmount = amount;
        annualAmount = monthlyAmount * 12;
        return //how do I return the values here to a CalculateCost function?
    }
    return new Tuple<double, double, double, double>(monthlyAmount, annualAmount, amount, users);
}


You could either use out parameters to return multiple values

OR

You could return a collection (e.g. a double array) or custom object which contains all the objects that you want to return.

class MyCustomType
{
  double AnnualAmount{get; set;}
  double MonthlyAmount{get; set;}
}

// and in your function you could go
return new MyCustomType{ AnnualAmount = 4.5d, MonthlyAmount = 5.5d  };


You can use an array, like this:

protected double[] CDCost() {
    //a lot of code
    return new double[2] { annualAmount, monthlyAmount };
}

and then use it like this:

double cdCost = CDCost();
annualAmount = cdCost[0];
monthlyAmount = cdCost[1];


Would have changed the way you do it now, to return a CDCost object:

public Class CDCost
{
  public double Monthly {get;set;}
  public double Annually {get return Monthly*12;}  

  public CDCost(double monthly)
  {
    Monthly=monthly;
  }
}

But if you're interested in c# syntax - you can use out or an array for example (Although I think both of them are bad design in this case).


In a technical, somewhat smartass sense, the answer is that you can't return more than one value from a function. A function either returns one value or doesn't return anything. This goes for your Javascript example--it's not actually returning two values, it's returning one value, which is an array of two elements.

That said, here are your options, as already pointed out by others:

1) Return an array. This is what you did in the Javascript example, and it would work anywhere else, too. The problems I see are two: one, you're limited to one type (and, if applicable, its subtypes). If you want to return, say, a double and a string, you'd need to make it return an array of Objects and cast to the appropriate type. Two, it makes for a fairly difficult-to-use API, because ordering is crucial. Any code consuming your function would need to know that returnValue[0] represented, say, the monthly amount and returnValue[1] represented the annual amount. Sure, you could say that in the documentation, but it's still a tricky API to use. And God help you if you (or someone else) changes the order of the return values for some reason (possibly through carelessness). That would be a horrible bug to track down.

2) Return a dictionary. This avoids the ordering problem, but it makes up for that by adding the key problem--anyone using your code would need to know that the dictionary returned used some specific set of keys (say, "monthlyAmount" and "annualAmount"). A typo, either in consuming code or in the method's body, would be a pain in the ass. And just like the array solution, you'd have to be sure to document the key values. It also suffers from the same type issue that the array solution poses. It would probably also be a bit less performant than the array solution, although it's questionable whether it would really make a noticable difference.

3) out parameters. This avoids the previous problems in that they can each be of any type and the compiler/IDE will catch any typos/bad assignments. However, in general I try to avoid out parameters (as does FxCop). Personally, they seem to be abusing the parameter syntax--parameters are for passing data into the function, and out parameters, while they do technically do that, are being used for return values rather than values to be used by the function. It also pollutes the method signature--it's n more parameters you need to pass, which is particularly annoying if you want a bunch of overloads (and you're not using .NET 4.0's optional/named parameters). On a much more minor note, it's a bit annoying to have to declare return values before they can be returned, i.e.

double outparam1, outparam2;
SomeFunction(out outparam1, out outparam2);
//use out parameters

4) Return an instance of a custom class. It's a bit ugly to add an entire class specifically for the return value of a single method, but this is probably the most elegant solution, in that it tells people using the method exactly what's returned and you can mix types (and the method doesn't have to take extra parameters just to be able to return things, as with out parameters, which makes the method calls a little cleaner). However, I wouldn't go overboard with this--if you find yourself with lots of methods that need to return multiple (very different) values (you shouldn't find yourself this way), then adding a new return class for each method would, I think, make your life much more hellish.

5) Not return multiple values. This may not always be an option, but if you find yourself wanting to return multiple values, I'd look hard at whether the values need to be returned together, or if you can split it into two calls (GetMonthlyAmount() and GetAnnualAmount(), for instance). Sometimes this wouldn't make sense, particularly if your method is does expensive things like make a database call.

tl;dr: when you want to return multiple values, first make sure that you can't split it into separate methods, then for a custom return class. Within reason.


I know this thread is old but just thought I'd add my 2 cents: What about some read-only class properties in the same class that contains the function (which could be turned into a Sub at that point)? This is to me the cleanest solution that makes sense....

in VB:

Private iAmount  As Integer
Public ReadOnly Property Amount() As Integer
    Get
        Return iAmount
    End Get
End Property

Private iMonthlyAmount  As Integer
Public ReadOnly Property MonthlyAmount() As Integer
    Get
        Return iMonthlyAmount
    End Get
End Property

Private iAnnualAmount  As Integer
Public ReadOnly Property AnnualAmount() As Integer
    Get
        Return iAnnualAmount
    End Get
End Property


I would say named touple is the new way to go (since C#7, apparently)

protected (double amount, double monthlyAmount, double annualAmount) CDCost()
    {
        double monthlyAmount;
        double annualAmount;
        double amount;
        double users = Convert.ToDouble(txtCD.Text);

        if (users > 0 && users < 100)
        {
            amount = users * 14.95;
            monthlyAmount = amount;
            annualAmount = monthlyAmount * 12;
            return (amount, monthlyAmount, annualAmount);
        }
        throw new NotImplementedException();
    }

and then you can call it implicitly using the deconstructor

    var (amount, monthlyAmount, annualAmount) = CDCost();
    


The best way to do this is to return a dictionary with the values

protected Dictionary<string, double> CDCost()
    {
        Dictionary<string,double> values = new Dictionary<string,double>();
        double monthlyAmount;
        double annualAmount;
        double amount;
        double users = Convert.ToDouble(txtCD.Text);

        if (users > 0 && users < 100)
    {
        amount = users * 14.95;
        values["monthlyAmount"] = amount;
        values["annualAmount"] = monthlyAmount * 12;
        return values
    }

}

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜