views:

1532

answers:

3

Previously, I ran into a problem trying to share a type definition between my ASMX webservice and my .aspx page (webclient)

http://stackoverflow.com/questions/667619/confused-on-c-array-of-objects-and-implicit-type-conversion

As I understand the advice, the "problem" this creates can be solved by copying the array of objects created in the client to a new array of objects as defined by the ASMX proxy class.

Being a rookie in C# I am still struggling with this simple task. Here are more parts of my code (the other fragments in the previous post remain unchanged):

... here is where I populate the "test data" I want to pass to the web service:

// create an array of MetaData objects
MetaData[] nvPairs = new MetaData[20];   // arbitrary length of 20 pairs

// create arbitrary MetaData objects in the array
nvPairs[0] = new MetaData("Grant Number", "2577-9912");
nvPairs[1] = new MetaData("OPEAnalyst", "Simpson");

... here I attempt a function to "copy" from "real" type defined in my TRIMBrokerUtil namespace (which I can't use completely because of the proxy) to the proxy version of that type:

protected TRIMBrokerASMXProxy.ASMXProxy.MetaData[] CopyMetaData(
    MetaData utilArray)
{
    TRIMBrokerASMXProxy.ASMXProxy.MetaData[] outArray = 
        new TRIMBrokerASMXProxy.ASMXProxy.MetaData[utilArray.Name.Length];
    int i;
    for (i = 0; i < utilArray.Name.Length; i++)
    {
        outArray[i].Name = utilArray.Name;
        outArray[i].Value = utilArray.Value;
    }
    return outArray;
}

... and then here is where I try to call that function (compiler flags 2 errors on this line:

TRIMBrokerASMXProxy.ASMXProxy.MetaData[] kvData = 
    CopyMetaData(metaDataArray);

Both of the compile errors below point to the same line:

Error 1 The best overloaded method match for '_Default.CopyMetaData(TRIMBrokerUtil.MetaData)' has some invalid arguments

Error 2 Argument '1': cannot convert from 'TRIMBrokerUtil.MetaData[]' to 'TRIMBrokerUtil.MetaData'

Am I close ?

+5  A: 

You've declared your parameter to be MetaData rather than MetaData[] - in other words it's not an array. You're then using utilArray.Name rather a lot, but it's not clear why.

I suspect you actually want:

protected TRIMBrokerASMXProxy.ASMXProxy.MetaData[]
    CopyMetaData(MetaData[] utilArray)
{
    TRIMBrokerASMXProxy.ASMXProxy.MetaData[] outArray = 
        new TRIMBrokerASMXProxy.ASMXProxy.MetaData[utilArray.Length];
    for (int i = 0; i < utilArray.Length; i++)
    {
        outArray[i] = new TRIMBrokerASMXProxy.ASMXProxy.MetaData();
        outArray[i].Name = utilArray[i].Name;
        outArray[i].Value = utilArray[i].Value;
    }
    return outArray;
}

By the way, you might want to consider a using directive to make this easier to read:

using ProxyMetaData = TRIMBrokerASMXProxy.ASMXProxy.MetaData;

...

protected ProxyMetaData[] CopyMetaData(MetaData[] utilArray)
{
    ProxyMetaData[] outArray = new ProxyMetaData[utilArray.Length];
    for (int i = 0; i < utilArray.Length; i++)
    {
        outArray[i] = new ProxyMetaData();
        outArray[i].Name = utilArray[i].Name;
        outArray[i].Value = utilArray[i].Value;
    }
    return outArray;
}

Another alternative is Array.ConvertAll:

ProxyMetaData[] output = Array.ConvertAll(input,
    metaData => new ProxyMetaData(metaData.Name, metaData.Value));

If you're not using C# 3 you can use an anonymous method for that. If ProxyMetaData doesn't have an appropriate constructor and you are using C# 3, you can use an object initializer:

ProxyMetaData[] output = Array.ConvertAll(input,
    metaData => new ProxyMetaData { metaData.Name, metaData.Value });

If you're stuck with C# 2 and no appropriate constructor, then:

ProxyMetaData[] output = Array.ConvertAll(input, delegate(MetaData metaData)
{
    ProxyMetaData proxy = new ProxyMetaData();
    proxy.Name = metaData.Name;
    proxy.Value = metaData.Value;
});

I think that's covered all the bases :)

Jon Skeet
All hail King Jon..+1. A quick question, I like using List<T> and then return using ToArray(). I like using lists since I dont like using indexes like i. Am I killing the performance by doing this?
Perpetualcoder
Well, you're hurting performance, but probably not significantly. If I'm converting from an array *to* an array I probably wouldn't go via a list, but as soon as there's any doubt about the length of the final result I'll use a list for convenience. Don't forget Array.ConvertAll though :)
Jon Skeet
Wow!! Where is the tip jar! You are a saint and so good to help me like this. I am learning slowly and I really thank you for your kind help.
John Galt
Oops..got past the compile problems discussed above and now I cannot build the proxy class because of this:TRIMBrokerUtil.MetaData cannot be serialized because it does not have a parameterless constructor.
John Galt
That's easy then - give it a parameterless constructor :)
Jon Skeet
Again, not sure. Here is what I wrote: namespace TRIMBrokerUtil { public class MetaData { protected string _Name; protected string _Value; public MetaData(string keyword, string setting) { this.Name = keyword; this.Value = setting; }
John Galt
You want a "public MetaData() {}" as well - that's a parameterless constructor.
Jon Skeet
Thanks. Adding that worked but I don't grasp the magic of it all. Serializing deals with conversion of messages into XML and my little object needs a "key" and a "value" for the constructor so its as if I add something that doesn't really get "used". But it works! Thanks again, Jon.
John Galt
+2  A: 

I would just use LINQ to do this:

TRIMBrokerASMXProxy.ASMXProxy.MetaData[] kvData =
    metaDataArray.Select(d => 
        new TRIMBrokerASMXProxy.ASMXProxy.MetaData(
            d.Name, d.Value)).ToArray();

Additionally, if you are using .NET 3.5, it means you can use WCF as well, which is what you should be using to generate the proxy. You would be able to attribute your TRIMBrokerASMXProxy.ASMXProxy.MetaData type with the DataContract attribute and the members being serialized with the DataMember attribute. Then, you would be able to define your contract with the actual type, and not have to perform conversion at all.

casperOne
So would I, but I'm not sure I'd suggest that to a C# rookie, especially when we don't know whether or not he's using .NET 3.5 :)
Jon Skeet
I just finished typing the exact same code before SO prompted that a new answer was posted :) +1
Perpetualcoder
@Jon maybe give the rookie motivation to get upto speed with things :)
Perpetualcoder
@Jon: That's the beauty of two separate answers, we can let the "rookie" (your designation, not mine) decide.
casperOne
Not my designation - the OP's: "Being a rookie in C# I am still struggling with this simple task." I've expanded my answer with another possibility, which is slightly more efficient than plain LINQ (and in .NET 2.0) - Array.ConvertAll.
Jon Skeet
@Jon: In the end, this is bad code, there should be no conversion between the proxy type and the original type, as those types should be in a separate assembly and shared between the server and client. The conversion code is code that never has to be written.
casperOne
Yes, where feasible. It's been a while since I've written a .NET web service though, so I didn't feel it was my place to make that call :)
Jon Skeet
A: 

You can also use Array.ConvertAll. I know youre relatively new to this so let me try to explain. It has 2 generic parameters. The first one being the type of the array it wants to convert(lets call it I). And the second one the type you want to convert to (lets call it O). It accepts an array of type I and returns an array of type O. The second parameter is a Converter delegate. Applying the naming we have its signature goes like.

delegate O Converter(I input);

The body of the delegate must contain the code necessary to do the conversion. Inside the ConvertAll function, the code iterates thru each of the values in the input array and passes then to the delegate. The value returned by the delegate is then stored into an output array. The output array is returned to the user once all values are converted.

using ProxyMetaData = TRIMBrokerASMXProxy.ASMXProxy.MetaData;

ProxyMetaData[] convertedArray = Array.ConvertAll<MetaData, ProxyMetaData>(utilArray, 
delegate(MetaData metaData)
{
 ProxyMetaData returnValue = new ProxyMetaData();
 returnValue.Name = metaData.Name;
 returnValue.Value = metaData.Value;
 return returnValue;
});
jake.stateresa
Thanks Jake. This makes sense and I'll try but with Jon's method I ran into yet another twist trying to rebuild my proxy class: TRIMBrokerUtil.MetaData cannot be serialized because it does not have a parameterless constructorYet I need a constructor with 2 params; 1 for the name, 1 for the value
John Galt
If I understand right, I would run into the same complaint using Array.ConvertAll too. I think I have run into a "bigger" issue perhaps??
John Galt