C#中的存在类型?

时间:2015-09-09 18:12:13

标签: c# polymorphism existential-type

我目前面临C#中的问题,我认为可以使用存在类型来解决。但是,我真的不知道它们是用C#创建还是模拟(使用其他构造)。

基本上我想要一些像这样的代码:

public interface MyInterface<T>
{
    T GetSomething();
    void DoSomething(T something);
}

public class MyIntClass : MyInterface<int>
{
    int GetSomething()
    {
        return 42;
    }

    void DoSomething(int something)
    {
        Console.Write(something);
    }
}

public class MyStringClass : MyInterface<string>
{
    string GetSomething()
    {
        return "Something";
    }

    void DoSomething(string something)
    {
        SomeStaticClass.DoSomethingWithString(something);
    }
}

接下来我希望能够遍历实现此接口的对象列表,但不关心它具有什么类型参数。像这样:

public static void DoALotOfThingsTwice(){
    var listOfThings = new List<MyInterface<T>>(){
        new MyIntClass(),
        new MyStringClass();
    };

    foreach (MyInterface<T> thingDoer in listOfThings){
        T something = thingDoer.GetSomething();
        thingDoer.DoSomething(something);
        thingDoer.DoSomething(something);
    }
}

这不会编译,因为T使用的MyIntClassMyStringClass使用的public static void DoALotOfThingsTwice(){ var listOfThings = new List<∃T.MyInterface<T>>(){ new MyIntClass(), new MyStringClass(); }; foreach (∃T.MyInterface<T> thingDoer in listOfThings){ T something = thingDoer.GetSomething(); thingDoer.DoSomething(something); thingDoer.DoSomething(something); } } 不同。

我认为这样的事情可以解决问题,但我不知道在C#中是否有一种有效的方法:

"use strict";

var LocalStorage = function () {
    this.getValue = function (key) {
        return browser.executeScript("return window.localStorage.getItem('" + key + "');");
    };

    this.get = function () {
        browser.executeScript("return window.localStorage;");
    };

    this.clear = function () {
        browser.executeScript("return window.localStorage.clear();");
    };
};

module.exports = new LocalStorage();

4 个答案:

答案 0 :(得分:5)

不能直接在C#中使用。

您可以删除类型安全性并使用非通用基本接口,并将其用于“通用”代码:

public interface MyInterface
{
    object GetSomething();
    void DoSomething(object something);
}

public interface MyInterface<T> : MyInterface
{
    T GetSomething();
    void DoSomething(T something);
}

或者使用dynamic(再次没有编译时类型安全性):

foreach (dynamic thingDoer in listOfThings)
{
    dynamic something = thingDoer.GetSomething();
    thingDoer.DoSomething(something);
    thingDoer.DoSomething(something);
}

或生成多个版本的处理程序并根据类型(How do I use reflection to call a generic method?)创建(可能使用缓存)(注意:您不能真正表达“任意对象列表”比List<object>更好或List<NonGenericBaseInterface>List<NonGenericBaseClass>):

foreach (object thingDoer in listOfThings)
{
   // get Do via reflection and create specific version based on 
   // thingDoer.GetType(), than invoke 
   // consider caching "methodForType" in Dictionary by type
   MethodInfo method = this.GetType().GetMethod("Do");
   MethodInfo methodForType = method.MakeGenericMethod(thingDoer.GetType());
   methodForType.Invoke(thingDoer, null);

}

void Do<T>( MyInterface<T> thingDoer)
{
    T something = thingDoer.GetSomething();
    thingDoer.DoSomething(something);
    thingDoer.DoSomething(something);
}

反射的替代方法是使用表达式树来构建类似的代码。

答案 1 :(得分:5)

由于DoALotOfThingsTwice并不依赖于T,您可以将其包装在Action中,并将其存储在列表中,例如

public static Action DoSomethingTwice<T>(this MyInterface<T> i)
{
    return () =>
    {
        T something = i.GetSomething();
        i.DoSomething(something);
        i.DoSomething(something);
    };
}

然后

var listOfThings = new List<Action>() {
    new MyIntClass().DoSomethingTwice(),
    new MyStringClass().DoSomethingTwice()
};

答案 2 :(得分:1)

实际上,这完全可以通过类型安全的方式实现,无需任何类型强制转换,也无需对接口函数的功能进行任何假设。

https://dotnetfiddle.net/buneul

using System;
using System.Collections;
using System.Collections.Generic;

public interface MyInterfaceFunc {
    void Call<T>(MyInterface<T> obj);
}

public interface MyInterface {
    void Generically(MyInterfaceFunc func);  // this is the key!
}

public interface MyInterface<T> : MyInterface
{
    T GetSomething();
    void DoSomething(T something);
}

public class MyIntClass : MyInterface<int>
{
    public int GetSomething()
    {
        return 42;
    }

    public void DoSomething(int something)
    {
        Console.Write(something);
    }

    public void Generically(MyInterfaceFunc func) {
        func.Call(this);
    }
}

public class MyStringClass : MyInterface<string>
{
    public string GetSomething()
    {
        return "Something";
    }

    public void DoSomething(string something)
    {
        Console.Write(something);
    }

    public void Generically(MyInterfaceFunc func) {
        func.Call(this);
    }
}
public class MyFunc : MyInterfaceFunc {
    public void Call<T>(MyInterface<T> thingDoer) {
        T something = thingDoer.GetSomething();
        thingDoer.DoSomething(something);
        thingDoer.DoSomething(something);
    }
}

public class Program {
    public static void Main(){
        var listOfThings = new List<MyInterface>(){
            new MyIntClass(),
            new MyStringClass()
        };

        foreach (MyInterface thingDoer in listOfThings){
            thingDoer.Generically(new MyFunc());
        }
    }
}

答案 3 :(得分:0)

由于我不知道您在实际域名中的根本问题是什么,因此我无法提供防弹解决方案。您应该考虑what's covariance and contravariance in generic parameters both on interfaces and delegates的努力,并尝试重构您的代码。

目前,我认为这应该是一个可能的解决方案:

public static void DoALotOfThingsTwice()
{
    var listOfThings = new List<object>
    {
        new MyIntClass(), new MyStringClass()
    };

    MyInterface<int> a;
    MyInterface<string> b;

    // During each iteration, check if the thing is a concrete 
    // implementation of your interface MyInterface<T>...
    foreach (object thingDoer in listOfThings)
    {           
        // ...and call MyInterface<T>.DoSomething method depending on 
        // the success of the cast to MyInterface<int> or 
        // MyInterface<string> 
        if ((a = thingDoer as MyInterface<int>) != null)
            a.DoSomething(38);
        else if((b = thingDoer as MyInterface<string>) != null)
            b.DoSomething("hello world");
    }
}