Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Type Interfaces #2427

Closed
JohannesMoersch opened this issue May 1, 2015 · 11 comments
Closed

[Feature Request] Type Interfaces #2427

JohannesMoersch opened this issue May 1, 2015 · 11 comments

Comments

@JohannesMoersch
Copy link

You could also call this a static interface, because what it would allow you to do is enforce the implementation of constructors and static methods (similar to Issue #154). I'm calling it a Type interface, because the runtime type object would actually be the one that implements the interface. For example:

public static interface ITestTypeInterface
{
    ITestTypeInterface(int value);
    static string Title { get; }
    static void DoSomething();
}
public class TestClass : ITestTypeInterface
{
    public static string Title { get { return "Test Title"; } }
    public static void DoSomething() { }
    public TestClass(int value) { }
}

This class could then be used as follows:

public class TestGeneric<T> where T : ITestTypeInterface
{
    public T Instance { get; private set; }
    public TestGeneric()
    {
        Console.WriteLine(T.Title);
        T.DoSomething();
        Instance = new T(12);
    }
}

Using this as a constraint for Generics is fairly straight forward, but I think its use for reflection is more interesting:

var types = Assembly.GetExecutingAssembly().GetTypes();
var testType = types.First(t => t is ITestTypeInterface) as ITestTypeInterface;
Console.WriteLine(testType.Title);
testType.DoSomething();
// Not sure what the syntax on this one would look like.
// Worst case, this isn't allowed, and you have to make a Create function.
var instance = new testType(3);

This would be possible if the type object for test class looked something like this:

class TestClassType : RuntimeType, ITestTypeInterface
{
    public string Title { get { return TestClass.Title; } }
    public void DoSomething() 
    {
        TestClass.DoSomething()
    }
    // Not sure what the constructor would look like
}

This interface would both enforce that certain statics be implemented in a class, and expose them as instance members on the type object. This second point is critical, as it would allow for significantly more type-safe work with reflection.

As an example use case, in an application I work on we have a list of views for modifying settings. I construct this list dynamically using reflection. Currently I have to count on certain static properties existing to get the title, and description (I could use attributes, but their presence still could not be enforced at compile time), and I have to use Activator.CreateInstance to instantiate instances. This feature would allow me to do all of this in a concrete and type-safe fashion. In addition to the more obvious relevance to Generics (as shown above), this feature would make dynamically composing an application via reflection way safer.

I understand that the interfaces on Types concept would involve significant CLR changes (maybe this isn't even really possible), but I hope I got across what it is I would like to be able to do. Maybe there is another way to accomplish this type safety within reflection. What are your thoughts?

@HaloFour
Copy link

HaloFour commented May 1, 2015

Sounds very Delphi.

The problem is as you described. Each defined runtime type isn't it's own class, as they are in Delphi. As such there is nothing to implement the interface or to own the vtable necessary for virtual dispatch or overriding in child types.

Generally the answer to this request is to use factory interfaces instead of relying on static methods to implement factories.

@JohannesMoersch
Copy link
Author

The problem with factories is that at some point I still either need to reference each type explicitly or use reflection to create each type in an unsafe way.

@HaloFour
Copy link

HaloFour commented May 1, 2015

There's no need for reflection and you can still reference the types generically, you just need to implement these services as instance methods rather than static methods.

public interface ITestTypeInterface { }

public interface ITestTypeFactoryInterface<T> where T : ITestTypeInterface {
    string Title { get; }
    void DoSomething();
    T Create(int value);
}

public class TestGeneric<TF, T>
    where TF : ITestTypeFactoryInterface<T>, new ()
    where T : ITestTypeInterface
{
    public T Instance { get; private set; }
    public TestGeneric() {
        TF factory = new TF();
        Console.WriteLine(factory.Title);
        factory.DoSomething();
        Instance = factory.Create(12);
    }
}

@JohannesMoersch
Copy link
Author

This only covers the case within a generic. If I'm instantiating objects via reflection (which is the case I'm more concerned about) I still have the same issue. Either I have to create my object unsafely, or I have to create my factory unsafely. Also, this doesn't allow me to get information from the type (like title or description) unless I put this data into the factory. I mentioned a use case for this in my original post.

@HaloFour
Copy link

HaloFour commented May 1, 2015

Well, dealing with reflection something is going to be unsafe either way. But it's the same problem, at some point something is created unsafely, whether that be a factory instance, or some kind of proxy class pretending to be a runtime type that is serving as a factory instance.

C# could probably just fake it just like Oxygene does (Delphi for .NET) but it won't be of nearly the same value as it is in Delphi given that any type's static methods can be accessed through an instance of it's type.

Anywho I'd be curious to get a C# language designers take on this. If Anders really found it useful I imagine that it would've been on the drawing board early on.

@JohannesMoersch
Copy link
Author

I'm not sure what you are saying. There wouldn't be anything unsafe about it if you can access the statics as instance members on the type (as I was suggesting). You cast it to the interface and then all property and functions calls (and constructor calls) on that type would be checked at compile time.

@HaloFour
Copy link

HaloFour commented May 1, 2015

Taken to its logical conclusion, complete with CLR changes, sure. And there are a few other issues in here for many of those exact features (static method generic constraints, static members in interfaces, #2402 ctors in interfaces).

Not saying that I disagree with the concept. I can think of several places in my code where it could've been useful.

This is also actually a dupe of #2204.

@JohannesMoersch
Copy link
Author

Issue #2204 doesn't include the reflection stuff. Static interfaces for constraints is great (and would allow me to be sure my reflection calls will succeed), but it still leaves me with unsafe code when creating objects through reflection.

@HaloFour
Copy link

HaloFour commented May 1, 2015

Doesn't mention it directly but it would need the same metaclass concept to function. Your reflection scenario effectively requires that metaclasses become a first class citizen in the CLR otherwise Type would never be considered an ITestTypeInterface otherwise.

@JohannesMoersch
Copy link
Author

I agree that the core concept is the same, but the additional use case I'm proposing could have a significant impact on implementation.

@gafter
Copy link
Member

gafter commented Nov 20, 2015

This is a dup of #154.

@gafter gafter closed this as completed Nov 20, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants