0

I have a project with many different auto-generated classes who share many of the same properties. But one of their "shared" properties is declared as an int in some, and a long in others:

public class Book {
    int Id { get; set; }
}

public class Movie {
    long Id { get; set; }
}

I would like to unite these two classes under a common interface:

public interface Media {
    int/long Id;
}

I understand from this question that I can't treat them as the same type, because they aren't.

Based on this question I thought I might declare the field as dynamic in the interface. But this gives me errors because the implementing classes declare the field as int or long, not as dynamic.

The classes are auto-generated from database tables which I do not have the ability to change, so I cannot make the classes all use long or all use int.

Is there an elegant, C-sharpy way to represent both an int and a long in one interface?

Sydney Park
  • 123
  • 1
  • 7
  • Write an interface that has one or the other, and [implement it explicitly](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation) in both classes. `dynamic` for this purpose would be madness. – 15ee8f99-57ff-4f92-890c-b56153 Oct 31 '19 at 17:50
  • 1
    Are you sure you can't hook into the auto-generation process somehow? – Blorgbeard Oct 31 '19 at 17:54
  • 1
    Building on @Blorgbeard answer - if you can annotate which database columns these properties map to, you could have "int _id" which maps to database column "Id", and another property "long Id => (long)_id". But - again - this relies on you being able to change how the files are "auto-generated" – chill94 Oct 31 '19 at 19:09
  • Forgive the error in type casting in the comment above.. You'd need to use Convert.ToInt64(_id) instead of (long)_id. – chill94 Oct 31 '19 at 19:20

2 Answers2

3

There is no way to represent both an int and a long in one interface.

It is like wanted having a car being at the same time a Smart and a Toyota.

I don't know if it will solve your problem but you can create a generic interface:

public interface IMedia<T>
where T : IComparable, IFormattable, IConvertible
{
  T Id { get; set; }
}

Usage:

public class Book : IMedia<int>
{
  public int Id { get; set; }
}

public class Movie : IMedia<long>
{
  public long Id { get; set; }
}

Because there is no true generic polymorphism in C#, it may can't help you... and even with, int (4 bytes) and long (8 bytes) are two different types.

About the lack of true generic polymorphism and the missing diamond operator in C#

Perhaps you can do something like this:

public abstract class ObjectWithId
{
  private long _Id;
  public int IdAsInt
  {
    get { return checked((int)_Id); }
    set { _Id = (long)value; }
  }
  public long IdAsLong
  {
    get { return _Id; }
    set { _Id = value; }
  }
}

public class Book : ObjectWithId
{
}

public class Movie : ObjectWithId
{
}

You can create an interface if you need:

public interface IObjectWithId
{
  public int IdAsInt { get; set; }
  public long IdAsLong { get; set; }
}

But you say classes are auto-generated... so can you modify them with interface or abstract parent?

And if you can, why not standardize Ids?

1

Similar to the previous answer, this is my solution to the issue. I have a DB with both int and long primary keys. I would like to access the Id when passing in a generic object T:

public interface IBaseModel<T>
    where T : struct
{
    T Id { get; set; }
}

My DTO base:

public abstract class BaseModel<T> : IBaseModel<T>
    where T : struct
{
    public T Id { get; set; }
}

DTOs with int Ids will then look like this

public class IntModel : BaseModel<int>
{
}

DTOs with long Ids will then look like this

public class LongModel : BaseModel<long>
{
}

Most importantly, when I want to use these DTOs as generic parameters in methods or classes:

static void DoSomething<T>(IBaseModel<T> printMe)
    where T : struct
{
    Console.WriteLine(printMe.Id);
}

Passing these models are now nice and generic:

IntModel intModel = new IntModel();
intModel.Id = 1;

LongModel longModel = new LongModel();
longModel.Id = 2;

DoSomething(intModel);
DoSomething(longModel);
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
b166er
  • 189
  • 7