C# does permit this sort of overloading, and it has some complex rules (see 7.4.2.2 in the spec for the details) to decide which is the better conversion, and therefore which function will be called. The rules are complex - I wrote our compiler conformance suites for this, and I still have to diagram it to get it right.

The answer is “Process(int i)“, and this happens because, the language always prefers conversions to the signed types over conversions to unsigned types (a preference one way or the other is required to make things coherent in the presence of both signed and unsigned types).

Behavior like this can be more than a little confusing to users of a class. As language designers, we have to make sure that there's a well-defined behavior in cases such as these, but that doesn't mean that you have to create such cases.

My advice:

Think deeply before overloading on two types that have conversions defined between them. If you do need to overload on numeric types - say you're writing something like String.Format - then you need to overload on all the types.