15

私は .NET 派なので、最初に Java のいくつかの概念について理解していることを主張させてください。間違っていたら訂正してください。

Java Generics は、限定されたワイルドカードの概念をサポートしています。

class GenericClass< ? extends IInterface> { ... }

...これは .NET のwhere制限に似ています。

class GenericClass<T> where T: IInterface { ... }

Java のClassクラスは型を記述し、 .NETクラスとほぼ同等ですType

ここまでは順調ですね。Class<T>しかし、T が限定されたワイルドカードである場合に、一般的に型指定された Java に十分に近い等価物を見つけることができません。これは基本的に、 が表す型に制限を課しClassます。

Javaで例を挙げましょう。

String custSortclassName = GetClassName(); //only known at runtime, 
                                           // e.g. it can come from a config file
Class<? extends IExternalSort> customClass 
    = Class.forName("MyExternalSort")
        .asSubclass(IExternalSort.class);  //this checks for correctness

IExternalSort impl = customClass.newInstance(); //look ma', no casting!

.NET で取得できる最も近いものは次のようなものです。

String custSortclassName = GetClassName(); //only known at runtime, 
                                           // e.g. it can come from a config file

Assembly assy = GetAssembly();             //unimportant 

Type customClass = assy.GetType(custSortclassName);
if(!customClass.IsSubclassOf(typeof(IExternalSort))){
    throw new InvalidOperationException(...);
}
IExternalSort impl = (IExternalSort)Activator.CreateInstance(customClass);

Java バージョンの方がきれいに見えます。.NET の対応物を改善する方法はありますか?

4

4 に答える 4

2

の拡張メソッドとカスタム ラッパー クラスを使用System.Typeすると、Java 構文にかなり近づけることができます。

注: Type.IsSubclassOf型がインターフェイスを実装しているかどうかのテストには使用できません。MSDN のリンクされたドキュメントを参照してください。代わりに使用できますType.IsAssignableFrom- 以下のコードを参照してください。

using System;

class Type<T>
{
    readonly Type type;

    public Type(Type type)
    {
        // Check for the subtyping relation
        if (!typeof(T).IsAssignableFrom(type))
            throw new ArgumentException("The passed type must be a subtype of " + typeof(T).Name, "type");

        this.type = type;
    }

    public Type UnderlyingType
    {
        get { return this.type; }
    }
}

static class TypeExtensions
{
    public static Type<T> AsSubclass<T>(this System.Type type)
    {
        return new Type<T>(type);
    }
}

// This class can be expanded if needed
static class TypeWrapperExtensions
{
    public static T CreateInstance<T>(this Type<T> type)
    {
        return (T)Activator.CreateInstance(type.UnderlyingType);
    }
}

インターフェイス分散を使用したさらなる改善

(パフォーマンスが評価された後、本番コードでのみ使用する必要があります。(同時!)キャッシュ辞書を使用することで改善できますConcurrentDictionary<System.Type, IType<object>)

Covariant type parametersC# 4.0 で導入された機能である と、実装する追加の typeinterface IType<out T>を使用するType<T>と、次のようなことが可能になります。

// IExternalSortExtended is a fictional interface derived from IExternalSort
IType<IExternalSortExtended> extendedSort = ...
IType<IExternalSort> externalSort = extendedSort; // No casting here, too.

次のこともできます。

using System;

interface IType<out T>
{
    Type UnderlyingType { get; }
}

static class TypeExtensions
{
    private class Type<T> : IType<T>
    {
        public Type UnderlyingType
        {
            get { return typeof(T); }
        }
    }

    public static IType<T> AsSubclass<T>(this System.Type type)
    {
        return (IType<T>)Activator.CreateInstance(
           typeof(Type<>).MakeGenericType(type)
        );
    }
}

static class TypeWrapperExtensions
{
    public static T CreateInstance<T>(this IType<T> type)
    {
        return (T)Activator.CreateInstance(type.UnderlyingType);
    }
}

InterfaceA関係のないインターフェースなどの間で(明示的に)キャストできるInterfaceBように:

var x = typeof(ConcreteAB).AsSubclass<InterfaceA>();
var y = (IType<InterfaceB>)x;

しかし、それは演習の目的に反します。

于 2013-07-22T19:57:33.397 に答える
1

C# ジェネリックは宣言サイトの差異であり、型パラメーターの差異は固定されています。

Javaはユースサイト分散であるため、宣言List<E>ができたら、3つの方法で使用できます

List<Number>           // invariant, read/write
List<+Number>          // covariant, read only
List<-NUmber>          // contravariant, write only

どちらのアプローチにも長所と短所があります。use-site アプローチは明らかにより強力ですが、プログラマーにとって難しすぎるという評判を得ています。かなり掴みやすいと思います

List<Integer> integers = ...;
List<+Number> numbers = integers;  // covariant

残念ながら、Java はまったくおぞましい構文を発明しました。

List<? extends Number>    //  i.e. List<+Number>

コードにこれらがいくつか含まれると、非常に見苦しくなります。あなたはそれを乗り越えることを学ばなければなりません。

さて、宣言サイトキャンプでは、どうやって同じクラスで 3 つの分散を達成するのでしょうか? より多くの型を持つことによって - a ReadOnlyList<out E>、 a WriteOnlyList<in E>、およびList<E>両方を拡張する a 。これはそれほど悪くはなく、より優れた設計であると言えます。ただし、型パラメーターが増えると見苦しくなる可能性があります。また、クラスの設計者がそれがバリアントで使用されることを予期していなかった場合、クラスのユーザーはクラスをバリアントで使用する方法がありません。

于 2013-01-09T17:12:10.857 に答える
0

「as」演算子を使用すると、少しきれいなバージョンを取得できます。

String custSortclassName = GetClassName();
Assembly assy = GetAssembly();
Type customClass = assy.GetType(custSortclassName);

IExternalSort impl = Activator.CreateInstance(customClass) as IExternalSort;
if(impl==null) throw new InvalidOperationException(...);

ただし、ここでは、タイプを確認する前にインスタンスを作成しています。これは、問題になる可能性があります。

于 2013-01-09T15:19:58.917 に答える
0

次のような拡張メソッドを作成してみてください。

 static class TypeExtension
    {
        public static I NewInstanceOf<I>(this Type t) 
            where  I: class 
        {
            I instance = Activator.CreateInstance(t) as I;
            if (instance == null)
                throw new InvalidOperationException();
            return instance;
        }
    }

これは、次の方法で使用できます。

String custSortclassName = GetClassName(); //only known at runtime, 
                                           // e.g. it can come from a config file

Assembly assy = GetAssembly();
Type customClass = assy.GetType(custSortclassName);            

IExternalSort impl = customClass.NewInstanceOf<IExternalSort>();
于 2013-01-16T03:01:29.397 に答える