tags:

views:

438

answers:

4

In my code I'm creating a number of lookup tables.

var Dict1 = data1.ToDictionary(dim => new { dim.Val1,dim.Val2,.. }  );
var Dict2 = data2.ToDictionary(dim => new { dim.Val1,dim.Val2,.. }  );

sometime there are duplicate key values, so I tried to use a catch block

try
{
   var Dict1 = data1.ToDictionary(dim => new { dim.Val1,dim.Val2,.. }  );
}
catch (ArgumentException ex)
{
    Console.WriteLine("Duplicate values in Data1 {0}",ex);
    throw;
}

but this approach means that the Dicts won't be visible to the rest of the code.

Any ideas on how to do this?

Edit: the intent of the catch block is to report which dictionary creation failed.

A: 

You could declare the Dict1 variable outside the try catch block.

Chris J
A: 

Is there a reason to use the try/catch block? You aren't adding any value to your program besides writing to the console. If you were doing actual error handling there, I could see solving this problem, but I would just let the exception happen and not try to catch it at all.

Bryan Watts
It's not for error handling, but diagnostics
Scott Weinstein
+2  A: 

Declare and add a single element above the try, and then add the rest inside. Since you're specifically worried about duplicate keys, adding the first key/item gets you the type without the risk.

EDIT: I think just inferring the type, without adding the first element, is slightly better. While the usage is a bit cumbersome, it'll make it easier to add remaining elements (as opposed to clearing the dictionary, and then adding - or having to add everything after the 1st one - really only cleanly doable if you're consuming an IEnumerable).

var dict1 = InferDictionary(new { Value1 = 0, Value2 = "string" }, new DataItem());
try {
  data1.AddToDictionary(
     dict1, 
     dim => new { Value1 = dim.Val1, Value2 = dim.Val2 }
  );
} catch ... {
    ...
}

static IDictionary<TKey, TValue> InferDictionary<TKey, TValue>(TKey keyPrototype, TValue valuePrototype) {
 return new Dictionary<TKey, TValue>();
}

Or, create a convenience function to catch the exception for you:

var dict1 = TryCatch(
  () => 
      data1.ToDictionary(dim => new { 
         Value1 = dim.Val1,
         Value2 = dum.Val2
      }
  , (ArgumentException ex) => {
      Console.WriteLine("Duplicate values in Data1 {0}", ex);
     // throw(ex) works as well, and shouldn't screw the callstack up much
     // But I happen to like making it explicit
     return false; 
  }
);

static TResult TryCatch<TResult, TException>(Func<TResult> @try, Func<TException, bool> @catch) where TException : Exception {
   try {
       return @try();
   } catch (Exception ex) {
       TException tEx = ex as TException;
       if (tEx != null && @catch(ex)) {
          // handled
       } else {
          throw;
       } 
   }
}

The caveat of this is that you can't call TryCatch<,> in the "natural" ways:

// Not enough info to infer TException
var d = TryCatch(() => DoStuff(), ex => true);

// Can't infer only TResult
var d = TryCatch<ArgumentException>(() => DoStuff(), ex => true);

which, since you can't specify TResult, forces you into the somewhat odd syntax of declaring TException on the lambda:

var d = TryCatch(() => DoStuff(), (ArgumentException ex) => true);
Mark Brackett
I can't declare the element, as it's an anoymous type, but love the code idea!
Scott Weinstein
You can use a helper function to get the Dictionary created. AAMOF, you could just create an empty Dictionary, which would probably be more sensible - at the ugly tax of just using a parameter as a "prototype". You'd need to swap your data.ToDictionary method to AddToDictionary though.
Mark Brackett
A: 

You're going to a lot of effort to avoid declaring a new type. Anonymous types are a convenience for when you have a very specific data construct that you're expecting never to use ever again in any other place in your code. In this case, you want to represent the same type in more than one place, so the best solution is probably to simply create a class to represent that type. You can make it a private nested class to avoid polluting your type space outside this class.

StriplingWarrior