Using the presence of the indexer as a parameter of the signature / contract / type parameter

As an example, I will use the SqlDataReader and DataRow classes: they both define the following index:

public object this[int columnIndex] { get; set; }

What is the lowest common denominator type to use as the type of the method parameter, so that both (and any other class implementing the same index) can be passed and used in the same way, for example:

 void DoSomething(??? indexedObject) { string foo = indexedObject[0].ToString(); // let ignore the presence of ToString() // for the purpose of this question } 

Is this an object ? What if the index object cannot be obtained from object (I think it is possible, although very unlikely).

If that matters, I am targeting .NET 3.5.

Change I am looking for some mandatory contracts that force subscribers to transfer objects that implement the mentioned indexer.

+4
source share
2 answers

Is this an object?

To a large extent. There is no general interface or class that you can use, so object is the only thing guaranteed in the general hierarchy.

What if the indexed object cannot be obtained from the object?

This is not possible in .NET. System.Object is the basic type of all types, and values ​​can always be considered as an object (even if this requires a box to work).

However, passing through an object will not provide access to the index, except through reflection.

The only direct way to do this is through dynamic , but that requires .NET 4 (and is not type safe, which means you can get exceptions at runtime).

A better approach would be to provide Func<T, int, string> , which allows you to specify how to extract the value on the call node:

 void DoSomething<T>(T object, Func<T, int, string> valueExtractor) { string foo = valueExtractor(object, 0); } 

Then call through:

 DoSomething(indexedObject, (o,i) => o[i].ToString()); 

This allows you to pass an object and a mechanism to retrieve the value based on the index on the call site, which works for any type.


Change regarding:

Edit: I'm looking for some forced contracts that force callers to pass objects that implement the mentioned indexer.

There is no built-in contract or interface that these types do not implement, nor any way to restrict a generic one based on the existence of an indexer. You will need a different approach, for example, my suggestion on using a delegate to retrieve a value.

+7
source

I would say that the solution for delegates of Reed Copsey Func<T> is the best way to go , but since you will need to define your own delegates in C # 3.5, Func<T> will not be available, it is very simple.

Edit - Oh, that was 3.5 not 4.0.

For what it's worth, here is another solution that, as I stated in the commentary, uses an interface to determine adapter types that understand how to access specialized type indexers, but allow the call site ( DoSomething ) to work with a common interface:

 void Main() { var t1 = new Type1(); // ie Sql Reader var t2 = new Type2(); // ie DataRow DoSomething(new Type1IndexAdapter(t1)); DoSomething(new Type2IndexAdapter(t2)); } public void DoSomething(ICanIndex indexer) { var r = indexer["test"]; } public interface ICanIndex { string this[string index]{get;} } public class Type1IndexAdapter : ICanIndex { public Type1 value; public Type1IndexAdapter(Type1 val) { this.value = val; } public string this[string index] { get { return this.value[index]; } } } public class Type2IndexAdapter : ICanIndex { public Type2 value; public Type2IndexAdapter(Type2 val) { this.value = val; } public string this[string index] { get { return this.value[index]; } } } public class Type1 // ie SqlDataReader { public string this[string index] { get { Console.WriteLine("Type 1 indexer called: " + index); return null; } } } public class Type2 // ie DataRow { public string this[string index] { get { Console.WriteLine("Type 2 indexer called: " + index); return null; } } } 
+1
source

Source: https://habr.com/ru/post/1481446/


All Articles