0

I've got two classes which has two common properties Id and SortNumber. I wanted to have a generic method to sort items in a list of said classes.

Is it possible to not implement the interface IOrderitem to classes Foo and Bar but still be able to use the method MoveUp?

Or is reflection the only alternative? Been writing mostly TypeScript code last few years so a bit rusty on C#.

public class Foo
{
   public int Id {get;set;}
   public int SortNumber {get;set;}
   // etc
} 

public class Bar
{
   public int Id {get;set;}
   public int SortNumber {get;set;}
   // etc
}

public interface IOrderitem
{
   int Id {get;set;}
   int SortNumber {get;set;}    
}

public static void MoveUp<T>(List<T> itemList, int id)
{
    for (int i = 0; i < itemList.Count; i++)
    {
        // reindex items
        var item = itemList[i] as IOrderItem;

        bool isItem = item.Id == id;

        if (isItem && i > 0)
        {
            // set item above eventinfo item to one position lower (0 = top rest lower)
            (itemList[i - 1] as IOrderItem).SortNumber = i;

            // set the item to move up one position higher (0 = top rest lower)
            item.SortNumber = i - 1;
            i++;
        }
        else
        {
            item.SortNumber = i;
        }
    }
}
Ralf de Kleine
  • 11,464
  • 5
  • 45
  • 87
  • 1
    This is normally the sort of thing you accomplish through `IComparable` and `IComparer`. (Not to mention that you usually don't reimplement sort logic, since we already have `.OrderBy` and indeed `List.Sort`.) – Jeroen Mostert Mar 21 '19 at 14:01
  • If you need the items sorted and ability to move up and down in the sort order maybe you could do better with a LinkedList? – Janne Matikainen Mar 21 '19 at 14:04
  • 2
    You're talking about duck typing. In C#, this is only possible with 2 approaches: Reflection or `dynamic`. Both are slow. I would avoid both unless absolutely necessary. – dymanoid Mar 21 '19 at 14:07
  • @JeroenMostert I'm not sorting but moving a item in the list then re-indexing that item. – Ralf de Kleine Mar 21 '19 at 14:45
  • @dymanoid if by slow it is a few milliseconds at most then it is no problem. – Ralf de Kleine Mar 21 '19 at 14:46
  • @RalfdeKleine It makes your programs very fragile and error prone, which is a much bigger deal than the (sometimes significant) performance costs. – Servy Mar 21 '19 at 15:51

2 Answers2

0

You could implement a base class for Foo and Bar and let inherit from it.

public class BaseClass
{
    public int Id { get; set; }
    public int SortNumber { get; set; }
    // etc
}
class Foo : BaseClass    {    }
class Bar : BaseClass    {    }

Instead of making List of T, where you have the defined type, you could use dynamic. But I would not recommend to do that. it's not recommended. See Link Is the use of dynamic considered a bad practice?

 public static void MoveUp(List<dynamic> itemList, int id)
    {
       // ....
    }

You can then use the "properties" as usual, but any renaming later, could lead to crashes.

Actually all depends on your purpose to NOT use an interface, which is not clear to me.

Malior
  • 1,221
  • 8
  • 16
  • I was wondering if it could be done without interfaces -or- baseclasses -or- abstract classes. I'm using EF classes which exist in a common project I'd rather not change. – Ralf de Kleine Mar 21 '19 at 14:48
  • @dymanoid there are some reasons. but please read here: https://stackoverflow.com/questions/31859016/is-the-use-of-dynamic-considered-a-bad-practice. And i wrote, that it's risky, and also why... no need to just downrate IMHO – Malior Mar 21 '19 at 14:56
  • @dymanoid: At that point i was not sure, if this would be an option at all. But you are right, I've edited this. – Malior Mar 21 '19 at 15:21
0

You write in the comments:

I was wondering if it could be done without interfaces -or- baseclasses -or- abstract classes. I'm using EF classes which exist in a common project I'd rather not change.

So, you want the duck typing feature.

C# is a strongly typed language. Duck typing is rather a dynamic language feature. Although there are some examples of duck typing in the C# compiler itself (see e.g. foreach or await), the extensive usage of duck typing breaks the purpose of the strongly typed language: compile time type safety.

There are two ways you can implement it in C#: using either Reflection or dynamic. Both are slow.

I can show you the dynamic approach.

In your MoveUp<T> method, use

var item = itemList[i] as dynamic;

instead of

var item = itemList[i] as IOrderItem;

So now, T can be even typeof(object). You can access the Id and SortNumber properties - they just need to exist on that object. But: this kinda drops all the benefits you get with the generics.

Drawbacks:

  • sloooow
  • no compile-time type safety
  • no automatic refactoring and other lovely stuff of IDEs
  • need to handle exceptions if there are no such properties or if they have wrong types or if they are read-only etc

I've created a simple benchmark for you. I have a List<Item> with 10.000 objects. I do:

for (int i = 0; i < 10_000; i++)
{
    sum += objects[i].Value;
}

and

for (int i = 0; i < 10_000; i++)
{
    dynamic o = objects[i] as dynamic;
    sum += o.Value;
}

The results are:

|     Method |      Mean |     Error |    StdDev |
|----------- |----------:|----------:|----------:|
| Direct     |  11.22 us | 0.0268 us | 0.0251 us |
| Dynamic    | 256.15 us | 0.9606 us | 0.8986 us |

So the dynamic approach is 23 times slower (on my machine, using .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3324.0).

dymanoid
  • 14,771
  • 4
  • 36
  • 64
  • Thanks. I wil not use it but you did answer my question! And learned about duck-typing. We will be using an interface and break open the common project, seems like the best solution (at this moment). – Ralf de Kleine Mar 22 '19 at 08:35