试图利用通用参数的组合

时间:2017-10-23 07:31:27

标签: c# .net generics

这令我感到困惑,因为我得到了看似矛盾的错误。

我使用泛型,将T约束为Something,然后将U约束为AnOperation<Something>

我预计对象AnOperation<Something>从现在开始被认为是类型U。但是,我收到了错误:

Cannot implicitly convert type 'ConsoleApp1.AnOperation<T>' to 'U'

这很奇怪。好吧,我试着明确地将它投射到U,然后我收到了这个错误:

Cannot convert type 'ConsoleApp1.AnOperation<T>' to 'U'也提到Cast is redundant

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }

    class MyClass<T, U>
        where T : Something
        where U : AnOperation<Something>
    {
        public U GetAnOperationOfSomething()
        {
            AnOperation<T> anOperation = new AnOperation<T>();

            return anOperation; // Cannot implicitly convert type 'ConsoleApp1.AnOperation<T>' to 'U'

            // return (U)anOperation; // Cannot convert type 'ConsoleApp1.AnOperation<T>' to 'U' also Cast is redundant
        }
    }

    public class Something
    {
    }

    public class AnOperation<T>
        where T : Something
    {
    }

}

这里发生了什么?

编辑:我试图了解语言级别的问题,而不是寻找实际问题的解决方法。

6 个答案:

答案 0 :(得分:11)

你的约束几乎是正确的,但并不完全。你定义

 where U : AnOperation<Something>

但是你创建了

AnOperation<T> anOperation = new AnOperation<T>()

这不是一回事。如果您将约束更改为...

 where U : AnOperation<T>

......你会没事的。

另一个问题是,虽然U每个AnOperation<T>都是AnOperation<T>,但并非每个U都是public U GetAnOperationOfSomething() 。当你申报......

U

...您正在保证该方法返回的是AnOperation<T>U无法满足这种保证。

您正在使用U的类型转换解决此问题。这违反了泛型类的目的,因为每个AnOperation<T>必须是U,否则您将获得运行时异常。这使得整个类型参数U变得不必要。您实际想要做的是创建new()。您可以使用class MyClass<T, U> where T : Something where U : AnOperation<T>, new() { public U GetAnOperationOfSomething() { U anOperation = new U(); //... return anOperation; } } 约束:

new()

U约束保证new将有一个公共默认构造函数,您可以调用它。

答案 1 :(得分:5)

您班级中的

T是继承自Something的任何类。 U是继承自AnOperation<Something>的任何类。假设您有这样的子类:

public class ChildOperation<T> : AnOperation<T> {}

现在您的U可能是ChildOperation<Something>,并且您无法返回父类(AnOperation<T>)的实例作为子类的实例(U,即ChildOperation<Something> })。 T确实ChildSomething时可能会出错,因为AnOperation<Something>无法隐式转换为AnOperation<ChildSomething>。长话短说 - AnOperation<T>确实无法始终转换为您的U类型,因此编译器是正确的。

答案 2 :(得分:1)

只是为了支持@Evk的回答。见这个例子。

class Program
{
    static void Main(string[] args)
    {
        MyClass<SomeSomething, AnOperation<Something>> foo = 
        new MyClass<SomeSomething, AnOperation<Something>>(); 
        // AnOperation<SomeSomething> will cause a compile error.

        var bar = foo.GetAnOperationOfSomething();

        Console.WriteLine(bar != null);

        Console.Read();
    }
}

class MyClass<T, U>
    where T : Something
    where U : AnOperation<Something>
{
    public U GetAnOperationOfSomething()
    {
        U anOperation = Activator.CreateInstance<U>();

        return anOperation;
    }
}

public class Something
{
}

public class AnOperation<T>
    where T : Something
{
}

public class SomeSomething : Something
{
}

答案 3 :(得分:1)

如果从AnOperation类切换到IAnOperation接口,它会令人惊讶地编译:

class MyClass<T, U>
    where T : Something
    where U : IAnOperation<Something>
{
    public U GetAnOperationOfSomething()
    {
        IAnOperation<T> anOperation = GenAnOperation();
        return (U)anOperation;
    }

    private IAnOperation<T> GenAnOperation()
    {
        throw new NotImplementedException();
    }
}

public class Something
{ }

public interface IAnOperation<T>
    where T : Something
{ }

答案 4 :(得分:1)

目前还不清楚你要做什么,你可能想扩大这个问题。例如,会有几种类型的操作吗?根据具体情况,有几种可能的解决方案

如果没有操作子类型,则不需要U参数。只需返回document.querySelector('#inputImage').addEventListener('change', function() { var reader = new FileReader(); reader.onload = function() { var arrayBuffer = this.result, array = new Uint8Array(arrayBuffer); // Replace the subscriptionKey string value with your valid subscription key. var subscriptionKey = "YOUR-KEY-HERE"; var uriBase = "https://YOUR-LOCATION-HERE.api.cognitive.microsoft.com/vision/v1.0/RecognizeText"; var params = { "handwriting": "true", }; $.ajax({ url: uriBase + "?" + $.param(params), beforeSend: function(jqXHR) { jqXHR.setRequestHeader("Content-Type", "application/octet-stream"); jqXHR.setRequestHeader("Ocp-Apim-Subscription-Key", subscriptionKey); }, type: "POST", processData: false, data: arrayBuffer }) .done(function(data, textStatus, jqXHR) { // Show progress. $("#responseTextArea").val("Handwritten text submitted. Waiting 10 seconds to retrieve the recognized text."); setTimeout(function() { // The "Operation-Location" in the response contains the URI to retrieve the recognized text. var operationLocation = jqXHR.getResponseHeader("Operation-Location"); $.ajax({ url: operationLocation, beforeSend: function(jqXHR) { jqXHR.setRequestHeader("Content-Type", "application/json"); jqXHR.setRequestHeader("Ocp-Apim-Subscription-Key", subscriptionKey); }, type: "GET", }) .done(function(data) { // Show formatted JSON on webpage. $("#responseTextArea").val(JSON.stringify(data, null, 2)); }) .fail(function(jqXHR, textStatus, errorThrown) { // Display error message. var errorString = (errorThrown === "") ? "Error. " : errorThrown + " (" + jqXHR.status + "): "; errorString += (jqXHR.responseText === "") ? "" : (jQuery.parseJSON(jqXHR.responseText).message) ? jQuery.parseJSON(jqXHR.responseText).message : jQuery.parseJSON(jqXHR.responseText).error.message; alert(errorString); }); }, 10000); }) .fail(function(jqXHR, textStatus, errorThrown) { // Put the JSON description into the text area. $("#responseTextArea").val(JSON.stringify(jqXHR, null, 2)); // Display error message. var errorString = (errorThrown === "") ? "Error. " : errorThrown + " (" + jqXHR.status + "): "; errorString += (jqXHR.responseText === "") ? "" : (jQuery.parseJSON(jqXHR.responseText).message) ? jQuery.parseJSON(jqXHR.responseText).message : jQuery.parseJSON(jqXHR.responseText).error.message; alert(errorString); }) } reader.readAsArrayBuffer(this.files[0]); }, false); ,因为它已经可以描述所有操作。

AnOperation<T>

如果有多种操作(似乎很可能),那么问题是你的班级在不知道它们时不知道如何制作它们。您需要为您的类提供一个可以生成 // With only one type of operation namespace ConsoleApp2 { class MyClass<T> where T : Something { public AnOperation<T> GetAnOperationOfSomething() { AnOperation<T> anOperation = new AnOperation<T>(); return anOperation; } } public class Something { } public sealed class AnOperation<T> where T : Something { } } 实例的函数。

U

或者,您可以将 // With a factory for operations namespace ConsoleApp1 { class MyClass<T, U> where T : Something where U : AnOperation<Something> { private readonly Func<T, U> operationMaker; public MyClass(Func<T, U> operationMaker) { this.operationMaker = operationMaker; } public U GetAnOperationOfSomething(T something) { U anOperation = operationMaker(something); return anOperation; } } public class Something { } public class AnOperation<T> where T : Something { } } 限制为新U,以便您的班级可以在不知情的情况下创建它们。

new()

答案 5 :(得分:0)

以下对我有用(编辑:我在框架v4.5.2中):

class Program
    {
        static void Main(string[] args)
        {
            MyClass<Something, AnOperation<Something>> obj = new MyClass<Something, AnOperation<Something>>();
        }
    }

    class MyClass<T, U>
        where T : Something
        where U : AnOperation<Something>
    {
        public U GetAnOperationOfSomething()
        {
            AnOperation<T> anOperation = new AnOperation<T>();

            return anOperation as U;

        }
    }

    public class Something
    {
    }

    public class AnOperation<T>
        where T : Something
    {
    }