noutarins
@noutarins (nou tarins)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

OrderByで型引数を明示的に指定してくださいのエラー

解決したいこと

C# Unity VSです。
Linq の .OrderByメソッドを使ったのですがそのメソッドの対する引数が推測できないと出てしまって困っています

発生している問題・エラー

重大度レベル  コード   説明  プロジェクト  ファイル    行 抑制状態
エラー   CS0411  メソッド 'Enumerable.OrderBy<TSource, TKey>(IEnumerable<TSource>, Func<TSource, TKey>)' の型引数を使い方から推論することはできません。型引数を明示的に指定してください。

または、問題・エラーが起きている画像をここにドラッグアンドドロップ

該当するソースコード

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

public class CreateSlotScript : MonoBehaviour
{




   private void SlotMaker(IEnumerable<WeponDate>weponDates)
    {
        foreach(var numbers in weponDates.OrderBy(x => x.GetobjectNumber))
        {

        }
    }
}

関係ありそうなソースも張っておきます。
私にはどこが明示的でないのかがわかりません。

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

[Serializable]
[CreateAssetMenu(fileName = "Wepon", menuName = "CreateWepondate")]
public class WeponDate : ItemBaseDate
{

    public enum KindOfWepon
    {
        Sword,
        GSword,
        Mace,
        Hammer,
        Bow,
        Wand,
        Shield,
    }

    [SerializeField]
    private int slot;
    [SerializeField]
    private int attack;
    [SerializeField]
    private KindOfWepon kindOfWepon;


    public int Getslot()
    {
        return slot;
    }

    public int GetAttack()
    {
        return attack;
    }

    public KindOfWepon GetKindOfWepon()
    {
        return kindOfWepon;
    }

   [SerializeField]
    private ItemDataBase itemDataBase;
    // アイテム数管理
    private Dictionary<WeponDate, int> numOfWepon = new Dictionary<WeponDate, int>();
    private Dictionary<AccesaryDate, int> numOfAccessory = new Dictionary<AccesaryDate, int>();
    private Dictionary<ProtectDate, int> numOfProtect = new Dictionary<ProtectDate, int>();
    private Dictionary<OtherItemDate, int> numOfOtherItem = new Dictionary<OtherItemDate, int>();

}

0

2Answer

エラーを出しているメソッドEnumerable.OrderBy<TSource, TKey>にはTSource,TKeyの2つの型引数が必要です。このうちTSourceprivate void SlotMaker(IEnumerable<WeponDate>weponDates)からWeponDateであることが推論可能です。
TKeyx.GetobjectNumberと書いているので通常ならWeponDate.GetobjectNumberプロパティの型を見て推論するはずなのですが…これ名前からしてメソッドじゃないですか?
メソッドの返り値でソートをしたいのであればx.GetobjectNumber()と書かなきゃですね。


補足。
「型引数を明示的に指定」するとこういう書き方になります。
weponDates.OrderBy<WeponDate, int>(x => x.GetobjectNumber)
実は普段目にしているLINQメソッドは、型引数を省略した形式なんです。

3Like

Comments

  1. @noutarins

    Questioner

    ありがとうございます。出来ました!
    プロパティとメソッドの違いについて勉強しなおしてみます!
  2. @noutarins

    Questioner

    ごめんなさい、あんまり関係ない質問なのですが、一枚目の、
    private void SlotMaker(IEnumerable<WeponDate>weponDates){}
    について質問なのですが、これは、私の中で<WeponDate>にIEnumerableという属性を付与していて、仮引数としてweponDatesを宣言している。と解釈しているのですが、それならば、private void SlotMaker(weponDates){IEnumerable<WeponDate>}みたいな感じでもいいのかと思ったのですがエラーが出てしまいます。
    どうしてなのでしょうか?

mrakdownで書きたいのでこちらにコメントします。

<WeponDate>にIEnumerableという属性を付与していて

逆です。
たとえばList<int>型のオブジェクトは「intをいくつでも(0個や1個もあり)入れられるオブジェクト」です。
クラスとしてはListであり、「型引数Tで指定した型のオブジェクトをいくつでも入れられる」となります。
クラスとしての実体はListの方で、型引数Tはどんな型のオブジェクトを入れられるのかを明示する追加情報です。
配列(Array)やList<T>など「同じ型のオブジェクトがいくつも入っているオブジェクト」はIEnumerable<T>インターフェイスを実装していて、引数の型がIEnumerable<T>となっているメソッドの引数として渡せます。
IEnumerable<WeponDate>というのは「どういう形で入っているかは決まってないけど、WeponDate型のオブジェクトがいくつも入っているオブジェクトで、その中身を一つずつ取り出すことができるもの」という意味になります。

OrderByで並べ替えできるということは、その対象は複数のWeponDate型オブジェクトだということですよね。(もしweponDatesの中身が0個や1個であっても「並べ替え前と後で同じもの」というだけなので並べ替え自体は行っても問題ありません)

int[] lst = {5, -3, -8, 0};//int[]はIEnumerable<int>でもある
foreach(var a in lst.OrderBy(
        x => System.Math.Abs(x)//このラムダ式はkeySelectorという名前
    ))
{
    Console.WriteLine(a);
}

上の例ではlstに4つの要素が入っているので、keySelectorは4回実行されます。
OrderByは「元のIEnumerable型のリストから要素を一つずつ取り出し、その要素一つずつに対してkeySelectorを実行、その返り値を見て昇順になるように並べ替える」という動作をします。

番目 xの値 keySelectorの返り値
1 5 5
2 -3 3
3 -8 8
4 0 0

OrderByはこの結果を見て「返り値が5,3,8,0だから、4番目、2番目、1番目、3番目の順番にすればいいんだな」と判断して、foreachのaには0,-3,5,-8の値を渡す4回ループになります。

1Like

Comments

  1. @noutarins

    Questioner

    同じことを言っていたらすいません。つまり、今回引数としたいのが<List>であり、<List>のような「同じ型のオブジェクトがいくつも入っているオブジェクト」は、引数として渡すときのために、「IEnumerable<T>の形にある引数として渡すことができる。」というインターフェースが実装されているため、渡すときは受け取るメソッド側でそう宣言してあげる必要があるということであってますか?
  2. たとえば「中身にWeponDateが入っている配列」なら引数の型は WeponDate[] と書きますよね。
    「中身にWeponDateが入っているList」なら List<WeponDate>、他にも HashSet<WeponDate>、Queue<WeponDate>、Stack<WeponDate>など「WeponDateがいくつも入っているシリーズ(コレクションクラス)」はどれも「IEnumerable<WeponDate>インターフェイスを持つオブジェクト」なので void SlotMaker(IEnumerable<WeponDate>weponDates) メソッドの引数として渡すことができます。
    「配列を受け取る場合のメソッド」「Listを受け取る場合のメソッド」などと同じようなものをいくつも作らなくていいので楽です。

    > 「IEnumerable<T>の形にある引数として渡すことができる。」というインターフェースが実装されているため

    IEnumerable<T>が「インターフェイス」です。C#のインターフェイスとは「あるクラスが決められたメソッドやプロパティを持っているように要求する規定」であり、その規定(たとえばIEnumerable<WeponDate>)を守っているクラスは「IEnumerable<WeponDate>を実装しているクラス」となります。
    メソッドのパラメータ宣言を「インターフェイス」と呼ぶこと自体は文章としておかしくないのですが、言語仕様として「インターフェイス」があるC#では混乱を招くので避けた方がいいです。

    > 渡すときは受け取るメソッド側でそう宣言してあげる必要がある

    「必要がある」というより「そう宣言することで楽になる」ですね。
    たとえば同じList<T>クラスのオブジェクトでも、たとえばList<int>を渡そうとすればエラーになります。
    歴史的ないきさつで型引数を持たない IEnumerable もあって、こちらは List<WeponDate> も List<int> も渡せます。(使うことはまずないです)
    中身の型がわからないので間違って渡してもエラーにならないし、中身の型がわからないので取り出した要素はいちいちキャストしないといけないしですごくめんどくさいのです…
    IEnumerable<WeponDate> はその入れ物が配列でもListでも気にする必要はないけど、その中に入っているオブジェクトはWeponDate型(またはnull)であることが保証されているので、取り出した中身のキャストや型チェックが不要になりすごく楽なんです。

    今回の場合も weponDates の中身がWeponDate型であると宣言しているから、x => x.GetobjectNumber を見てコンパイラが、「xの型はWeponDateだな、ということはWeponDate.GetobjectNumberを見てソートすればいいのか。あれ?GetobjectNumberってメソッドじゃないか。これじゃソートできないよ!」と気づいてくれたのです。

    「実行してみてはじめてエラーになる」は「ビルドの時点でエラーになる」よりも格段にめんどくさくて原因を探すのが大変ですからね…
  3. @noutarins

    Questioner

    何から何まで指摘してくださり本当にありがとうございます。
    そのような正確な理解はどのようにして身についたものなのですか?
    今の私のようにわからないことを質問し続けていればいつか身に付きますか?

Your answer might help someone💌